/*ScianVisObjects.c
  May 28, 1991
  Eric Pepke
  Visualization objects in SciAn*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianArrays.h"
#include "ScianWindows.h"
#include "ScianTextBoxes.h"
#include "ScianButtons.h"
#include "ScianTitleBoxes.h"
#include "ScianObjWindows.h"
#include "ScianIcons.h"
#include "ScianColors.h"
#include "ScianControls.h"
#include "ScianLists.h"
#include "ScianSpaces.h"
#include "ScianLights.h"
#include "ScianSliders.h"
#include "ScianIDs.h"
#include "ScianVisWindows.h"
#include "ScianDatasets.h"
#include "ScianPictures.h"
#include "ScianDialogs.h"
#include "ScianEvents.h"
#include "ScianScripts.h"
#include "ScianErrors.h"
#include "ScianComplexControls.h"
#include "ScianMethods.h"
#include "ScianStyle.h"
#include "ScianVisObjects.h"
#include "ScianVisIso.h"
#include "ScianVisContours.h"
#include "ScianVisTraces.h"
#include "ScianVisPoints.h"
#include "ScianVisSticks.h"
#include "ScianVisNumbers.h"
#include "ScianFilters.h"
#include "ScianDraw.h"
#include "ScianObjFunctions.h"
#include "ScianTemplates.h"
#include "ScianTemplateHelper.h"
#include "ScianSymbols.h"
#include "ScianGeometry.h"

#define AXISFACTOR	0.15		/*Factor of MAXSIZE to move axis name*/
#define MAJORTICSIZE	0.05		/*Size of major tics*/
#define LABELFACTOR	2.0		/*Offset of label*/
#define MINORTICFACTOR	0.6		/*Size of minor tics*/
#define BOUNDSEXPFACTOR	0.05		/*Factor to expand bounds for big bounds*/

ObjPtr visClass;			/*Class for all visualization objects*/
ObjPtr visBounded;			/*Class for all bounded vis objects*/
ObjPtr visAxes;				/*Class for all vis objects that can draw axes*/
ObjPtr visWalls;			/*Class for all vis objects that have walls*/
ObjPtr visSurface;			/*Class for all objects w/surface*/
ObjPtr visDeformed;			/*Class for all objects w/deformed surface*/
ObjPtr visColored;			/*Class for all objects colored*/
ObjPtr visGeometryClass;		/*Class for all objects with geometry*/
ObjPtr geoPictureClass;			/*Class for geometry picture*/
ObjPtr visIcon;				/*Icon for vis object*/
ObjPtr visLines;			/*Class for all objects w/lines*/
ObjPtr visDots;				/*Class for all objects w/dots*/

real globalFactor, globalOffset, globalFixed;
ObjPtr globalDeformObject;
extern ObjPtr perspecControlClass;	/*Perspective control*/
extern real spaceTime;
ObjPtr allVisObjClasses;

/*Mapping from data to visualization techniques*/
typedef struct
    {
	long dataInfo;			/*Info bits of the data*/
	int topDim;			/*Topological dimension of the data iff HAS_FORM, -1 for don't care*/
	int spatialDim;			/*Spacial dimension of the data iff HAS_FORM, -1 for don't care*/
	int nComponents;		/*N components of the data, -1 for don't care.*/
	ObjPtr visClass;
    } VisMapping;

#define MAXNVISMAPPINGS	200		/*Maximum number of visualization maps*/
int nVisMappings = 0;			/*Actual number of visualization maps*/
VisMapping visMapping[MAXNVISMAPPINGS];

/*Serial number generator for visualization types*/
typedef struct
    {
	char *name;
	int regSerial;
	int tempSerial;
    } VisSerial;
#define MAXNVISSERIALS	100
int nVisSerials = 0;
VisSerial visSerials[MAXNVISSERIALS];


/*Internal prototypes*/
#ifdef PROTO
static Bool GetTicParams(ObjPtr object, real lowTic[3], int nTics[3], 
	real ticSpace[3], int initMinor[3], int majorEvery[3]);
#endif

float material[30];			/*Random material*/

void SetupDeformation(object)
ObjPtr object;
/*Sets up to do deformation based on object*/
{
    ObjPtr var;

    /*Get factor and offset*/
    var = GetVar(object, DEFFACTOR);
    if (var)
    {
	globalFactor = GetReal(var);
    }
    else
    {
	globalFactor = 1.0;
    }
    var = GetVar(object, DEFOFFSET);
    if (var)
    {
	globalOffset = GetReal(var);
    }
    else
    {
	globalOffset = 0.0;
    }

    globalDeformObject = GetVar(object, DEFORMOBJ);

    /*Make it deformed or not according to DEFORMSWITCH and DEFORMOBJ*/
    if (!GetPredicate(object, DEFORMSWITCH))
    {
	globalDeformObject = NULLOBJ;
    }

    if (globalDeformObject)
    {
	SetCurField(DEFORMFIELD, globalDeformObject);
	SetCurForm(DEFORMFORM, globalDeformObject);
    }

    var = GetVar(object, DEFCONSTANT);
    if (var)
    {
	globalFixed = GetReal(var);
    }
    else
    {
	globalFixed = 0.0;
    }
}

int FindVisSerial(name)
char *name;
/*Finds a vis serial, returns it or -1*/
{
    int retVal;

    for (retVal = 0; retVal < nVisSerials; ++retVal)
    {
	char *s1, *s2;

	/*See if the name maps onto the first part of the object name*/
	s1 = visSerials[retVal] . name;
	s2 = name;
	while (*s1)
	{
	    if (toupper(*s1) != toupper(*s2)) break;
	    ++s1;
	    ++s2;
	}
	if (!(*s1))
	{
	    return retVal;
	}
    }
    return -1;
}

void DefineVisMapping(dataInfo, topDim, spatialDim, nComponents, visClass)
long dataInfo;
int topDim, spatialDim, nComponents;
ObjPtr visClass;
/*Defines a mapping from a dataset class to a visualization class.
  The first such mapping for a certain dataClass defines the preferred one.*/
{
    ObjPtr var;
    visMapping[nVisMappings] . dataInfo = dataInfo;
    visMapping[nVisMappings] . topDim = topDim;
    visMapping[nVisMappings] . spatialDim = spatialDim;
    visMapping[nVisMappings] . nComponents = nComponents;
    visMapping[nVisMappings] . visClass = visClass;
    ++nVisMappings;

    if (WhichListIndex(allVisObjClasses, visClass) < 0)
    {
	PostfixList(allVisObjClasses, visClass);
    }

    /*See if it needs a new serial record*/
    var = GetVar(visClass, NAME);

    if (var)
    {
	if (FindVisSerial(GetString(var)) < 0)
	{
	    /*Need a new one*/
	    visSerials[nVisSerials] . name =
		malloc(strlen(GetString(var)) + 1);
	    strcpy(visSerials[nVisSerials] . name, GetString(var));
	    visSerials[nVisSerials] . regSerial = 0;
	    visSerials[nVisSerials] . tempSerial = 0;
	    ++nVisSerials;
	}
    }
}

static ObjPtr MakeVisName(vis)
ObjPtr vis;
/*Makes the name of a visualization object*/
{
    Bool templatep;
    int visSerial;
    ObjPtr var;
    char *name;

    templatep = GetPredicate(vis, TEMPLATEP);
    var = GetVar(vis, NAME);
    if (var)
    {
	name = GetString(var);
    }
    else
    {
	name = "Visualization";
    }
    visSerial = FindVisSerial(name);
    if (visSerial >= 0)
    {
	if (templatep)
	{
	    sprintf(tempStr, "%s Template %d",
		visSerials[visSerial] . name, ++(visSerials[visSerial] . tempSerial));
	}
	else
	{
	    sprintf(tempStr, "%s %d",
		visSerials[visSerial] . name, ++(visSerials[visSerial] . regSerial));
	}
	SetVar(vis, NAME, NewString(tempStr));
    }
    return ObjTrue;
}

#ifdef PROTO
ObjPtr GetAllVis(ObjPtr dataSet, Bool justOne, ObjPtr class)
#else
ObjPtr GetAllVis(dataSet, justOne, class)
ObjPtr dataSet;
Bool justOne;
ObjPtr class;
#endif
/*Returns a list of all visualizations that can visualize dataSet, or NULLOBJ
  if justOne, returns the first it finds.  If class is not null, only 
  returns visualizations belonging to that class*/
{
    int k;
    long flags;
    int topDim, spatialDim, nComponents;
    ObjPtr var;
    ObjPtr lastVis = 0;
    ObjPtr list = NULLOBJ;

    flags = GetDatasetInfo(dataSet);
    flags &= ~DS_TIMEDEPENDENT;
    spatialDim = GetSpatialDim(dataSet);
    topDim = GetTopDim(dataSet);
    if (flags & DS_VECTOR)
    {
	var = GetIntVar("GetPrefVis", dataSet, NCOMPONENTS);
	if (!var)
	{
	    return NULLOBJ;
	}
	nComponents = GetInt(var);
    }

    /*Check all the possible visualization techniques on the raw dataset*/
    for (k = 0; k < nVisMappings; ++k)
    {
	if ((visMapping[k] . dataInfo & ~DS_TIMEDEPENDENT) != flags)
	{
	    continue;
	}
	if (visMapping[k] . topDim >= 0 && visMapping[k] . topDim != topDim)
	{
	    continue;
	}
	if (flags & DS_HASFORM)
	{
	    if (visMapping[k] . spatialDim >= 0 && visMapping[k] . spatialDim != spatialDim)
	    {
		continue;
	    }
	}
	if (flags & DS_VECTOR)
	{
	    if (visMapping[k] . nComponents >= 0 && visMapping[k] . nComponents != nComponents)
	    {
		continue;
	    }
	}
	if (visMapping[k] . visClass == lastVis)
	{
	    continue;
	}
	if (class && (visMapping[k] . visClass != class))
	{
	    continue;
	}
	lastVis = visMapping[k] . visClass;
	if (!list)
	{
	    list = NewList();
	}

	PostfixList(list, NewVis(dataSet, visMapping[k] . visClass));
	if (justOne)
	{
	    return list;
	}
    }

    lastVis = 0;
    return list;
}

ObjPtr GetPrefVis(dataSet)
ObjPtr dataSet;
/*Gets the preferred visualization for dataSet*/
{
    ObjPtr prefVis, list;

    if (prefVis = GetVar(dataSet, PREFVIS))
    {
	return NewVis(dataSet, prefVis);
    }

    if (list = GetAllVis(dataSet, true, NULLOBJ))
    {
	ThingListPtr listOf;
	listOf = LISTOF(list);
	if (listOf)
	{
	    return listOf -> thing;
	}
    }
    return NULLOBJ;
}

ObjPtr NewVis(dataset, visClass)
ObjPtr dataset, visClass;
/*Visializes dataset as a visualization object of class visClass.  If visClass
  is NULLOBJ, picks the preferred visualization type.*/
{
    ObjPtr retVal;
    FuncTyp method;

    if (!visClass)
    {
	return GetPrefVis(dataset);
    }

    retVal = NewObject(visClass, 0);
    if (!retVal)
    {
	return NULLOBJ;
    }

    method = GetMethod(retVal, SETMAINDATASET);
    if (method)
    {
	(*method)(retVal, dataset);
    }
    else
    {
	/****UPDATE*** compatibility with obselete visualization objects*/
	SetVar(retVal, REPOBJ, dataset);
	SetVar(retVal, MAINDATASET, dataset);
    }
    SetVar(retVal, PARENTS, NewList());
    method = GetMethod(retVal, INITIALIZE);
    if (method)
    {
	(*method)(retVal);
    }

    return retVal;
}

void CannotDropInMainDataset()
{
    WinInfoPtr errWindow;
    errWindow = AlertUser(UIERRORALERT, (WinInfoPtr) 0,
	"You cannot replace a visualization object's main dataset.",
        0, 0, "Oh well");
    SetVar((ObjPtr) errWindow, HELPSTRING,
        NewString("The main dataset used by a visualization object is used to \
determine its basic shape and dimensionality and is also used to help determine \
the controls in its control panel.  Unlike secondary datasets, such as the \
ones that give color, it cannot \
be changed after the visualization object is created.  If you would like to \
visualize the new dataset, create a new visualization object."));
}

void CannotColorByGeometry()
{
    WinInfoPtr errWindow;
    errWindow = AlertUser(UIERRORALERT, (WinInfoPtr) 0,
	"A geometry set cannot be used to color an object other than itself.",
        0, 0, "Oh well");
    SetVar((ObjPtr) errWindow, HELPSTRING,
        NewString("You have tried to color a visualization object with a \
geomety object.  This will not work, unless the geometry is the same as the main dataset of the visualization object ."));
}

void CannotDeformByGeometry()
{
    WinInfoPtr errWindow;
    errWindow = AlertUser(UIERRORALERT, (WinInfoPtr) 0,
	"A geometry set cannot be used to deform a visualization object.",
        0, 0, "Oh well");
    SetVar((ObjPtr) errWindow, HELPSTRING,
        NewString("You have tried to deform a visualization object with a \
geomety object.  This will not work."));
}

ObjPtr DropInMainDatasetCorral(corral, object, x, y)
ObjPtr corral, object;
int x, y;
/*Drops an icon in a main dataset corral*/
{
    DoTask(CannotDropInMainDataset);
    return ObjTrue;
}

static ObjPtr DeleteVisObjectIcon(icon)
ObjPtr icon;
/*Deletes a visualization object associated with an icon*/
{
    ObjPtr space, contents, clock, parents, repObj;

    repObj = GetObjectVar("DeleteVisObjectIcon", icon, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    space = GetVar(icon, SPACE);
    if (!space)
    {
	return ObjFalse;
    }

    SetVar(repObj, SPACE, NULLOBJ);
    contents = GetListVar("DeleteVisObjectIcon", space, CONTENTS);
    if (!contents)
    {
	return ObjFalse;
    }
    DeleteFromList(contents, repObj);
    parents = GetVar(repObj, PARENTS);
    if (parents)
    {
	DeleteFromList(parents, space);
    }
    ImInvalid(space);

    /*Reinitialize the clock in the space*/
    clock = GetVar(space, CLOCK);
    if (clock)
    {
	ReinitController(clock);
    }
    return ObjTrue;
}

ObjPtr NewVisIcon(visObject)
ObjPtr visObject;
/*Creates a new visualization icon for visObject*/
{
    ObjPtr name, defaultIcon, mainDataset;
    ObjPtr retVal;

    defaultIcon = GetObjectVar("NewVisIcon", visObject, DEFAULTICON);
    if (!defaultIcon)
    {
	return NULLOBJ;
    }
    retVal = NewObject(defaultIcon, 0);
    if (!retVal)
    {
	return NULLOBJ;
    }
    SetMethod(retVal, DELETEICON, DeleteVisObjectIcon);

    SetVar(retVal, REPOBJ, visObject);

    MakeVar(visObject, NAME);
    name = GetVar(visObject, NAME);
    SetVar(retVal, NAME, name);

    mainDataset = GetObjectVar("NewVisIcon", visObject, MAINDATASET);
    if (mainDataset)
    {
	name = GetLongName(mainDataset);
	SetVar(retVal, FORMAT, name);
    }
    SetVar(retVal, ICONLOC, NULLOBJ);

    return retVal;
}

static ObjPtr MakeFormBounds(object)
ObjPtr object;
/*Get bounds for an form-based object*/
{
    ObjPtr repObj, var;
    real bounds[6];

    repObj = GetVar(object, MAINDATASET);
    if (!repObj) repObj = GetObjectVar("FormBounds", object, REPOBJ);
    if (repObj)
    {
	ObjPtr formObj;
	MakeVar(repObj, DATAFORM);
	formObj = GetVar(repObj, DATAFORM);
	if (formObj)
	{
	    long info;
	    info = GetDatasetInfo(repObj);
	    
	    {
		ObjPtr curBoundsArray;

	 	curBoundsArray = GetArrayVar("FormBounds", formObj, BOUNDS);
		if (!curBoundsArray)
		{
		    return ObjFalse;
		}
		if (RANK(curBoundsArray) != 1)
		{
		    ReportError("FormBounds", "Malformed BOUNDS\n");
		    return ObjFalse;
		}
		if (DIMS(curBoundsArray)[0] == 6)
		{
	    	    Array2CArray(bounds, curBoundsArray);
		}
		else if (DIMS(curBoundsArray)[0] == 4)
		{
	    	    Array2CArray(bounds, curBoundsArray);
		    bounds[4] = bounds[5] = 0.0;
		}
		else
		{
		    return ObjFalse;
		}
	    }
	}
	else
	{
	    return ObjFalse;
	}
	var = NewRealArray(1, 6L);
	CArray2Array(var, bounds);
	SetVar(object, BOUNDS, var);
	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

static ObjPtr PrefixColoredDatasets(list, visObject)
ObjPtr list, visObject;
/*Prefixes the datasets in visObject to list*/
{
    ObjPtr colorObj; 
    if (colorObj = GetVar(visObject, COLOROBJ))
    {
	PrefixList(list, colorObj);
    }
}

static ObjPtr MakeGeoPictureBounds(object)
ObjPtr object;
/*Makes bounds for a geopicture*/
{
	ObjPtr geometry, repObj, objBounds;
	real *boundsPtr;
	real bounds[6];

	repObj = GetObjectVar("GeoPictureBounds", object, REPOBJ);
	if (!repObj)
	{
	    return ObjFalse;
	}

	MakeVar(repObj, CURDATA);
	geometry = GetVar(repObj, CURDATA);
	if (!geometry)
	{
	    ReportError("MakeGeoPictureBounds", "No geometry.");
 	    return ObjFalse;
   	}
    if (IsPicture(geometry))
    {

	GetPictureBounds(geometry, bounds);

	geometry = GetVar(repObj, ETERNALPART);
	if (geometry)
	{
	    real newBounds[6];
	    GetPictureBounds(geometry, newBounds);

	    bounds[0] = MIN(bounds[0], newBounds[0]);
	    bounds[1] = MAX(bounds[1], newBounds[1]);
	    bounds[2] = MIN(bounds[2], newBounds[2]);
	    bounds[3] = MAX(bounds[3], newBounds[3]);
	    bounds[4] = MIN(bounds[4], newBounds[4]);
	    bounds[5] = MAX(bounds[5], newBounds[5]);
	}

	objBounds = NewRealArray(1, (long) 6);
	CArray2Array(objBounds, bounds);
	SetVar(object, BOUNDS, objBounds);
    }
    else if (IsGeometry(geometry))
    {
	MakeFormBounds(object);
    }

    return ObjTrue;
}

static ObjPtr ChangeReflection(object)
ObjPtr object;
/*Changed value for a reflection quality control*/
{
    real shininess, specularity;
    ObjPtr val;
    ObjPtr repObj;

#ifdef INTERACTIVE
    interactiveMoving = false;
#endif
    repObj = GetObjectVar("ChangeReflection", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    if (!GetXYControlValue(object, &shininess, &specularity))
    {
	return ObjFalse;
    }

    SetVar(repObj, SHINVAL, NewReal(shininess));
    SetVar(repObj, SPECVAL, NewReal(specularity));
    DrawMe(repObj);
    return ObjTrue;
}

static ObjPtr ChangeBrightness(object)
ObjPtr object;
/*Changed value for a brightness slider*/
{
    real brightness;
    ObjPtr val;
    ObjPtr repObj;

#ifdef INTERACTIVE
    interactiveMoving = false;
#endif
    repObj = GetObjectVar("ChangeBrightness", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(object);
    if (val)
    {
	brightness = GetReal(val);
    }
    else
    {
	brightness = 1.0;
    }

    SetVar(repObj, BRIGHTNESS, NewReal(brightness));

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeBBFlag(object)
ObjPtr object;
/*Changed value for a flags check box*/
{
    int flagOn;
    ObjPtr val;
    ObjPtr repObj;
    ObjPtr flagsObj, whichFlagObj;
    int flags, whichFlag;

    repObj = GetObjectVar("ChangeBBFlag", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    flagsObj = GetVar(repObj, BBFLAGS);
    if (flagsObj)
    {
	flags = GetInt(flagsObj);
    }
    else
    {
	flags = 0;
    }

    val = GetVar(object, VALUE);
    if (val)
    {
	flagOn = GetInt(val);
    }
    else
    {
	flagOn = false;
    }

    whichFlagObj = GetIntVar("ChangeBBFlag", object, WHICHFLAG);
    if (whichFlagObj)
    {
	whichFlag = GetInt(whichFlagObj);
    }
    else
    {
	return ObjFalse;
    }

    if (flagOn)
    {
	flags |= whichFlag;
    }
    else
    {
	flags &= ~whichFlag;
    }

    SetVar(repObj, BBFLAGS, NewInt(flags));
    DrawMe(repObj);
    return ObjTrue;
}

ObjPtr objectForRGB;

void ObjectRGB(visWindow)
WinInfoPtr visWindow;
/*Sends a deferred message to visWindow if it has objectForRGB*/
{
    ObjPtr space;
    ObjPtr contents;

    space = FindSpace(visWindow);
    if (space)
    {
	contents = GetVar(space, CONTENTS);
	if (contents)
	{
	    ThingListPtr runner;

	    runner = LISTOF(contents);
	    while (runner)
	    {
		if (runner -> thing == objectForRGB)
		{
		    DeferMessage((ObjPtr) visWindow, SETRGBMESSAGE);
		}
		runner = runner -> next;
	    }
	}
    }
}

static ObjPtr ChangeTransparency(object)
ObjPtr object;
/*Changed value for a transparency checkbox*/
{
    int isTransparent;
    ObjPtr val;
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeTransparency", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(object);
    if (val)
    {
	isTransparent = GetInt(val);
    }
    else
    {
	isTransparent = false;
    }

    if (isTransparent)
    {
#ifdef GRAPHICS
	if (hasRGB)
	{
	    objectForRGB = repObj;
	    ForAllVisWindows(ObjectRGB);
	}
#endif
    }

    SetVar(repObj, ISTRANSPARENT, NewInt(isTransparent));
    DrawMe(repObj);
    return ObjTrue;
}

static ObjPtr ChangeTranslucency(object)
ObjPtr object;
/*Changed value for a translucency checkbox*/
{
    int isTranslucent;
    ObjPtr val;
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeTranslucency", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetVar(object, VALUE);
    if (val)
    {
	isTranslucent = GetInt(val);
    }
    else
    {
	isTranslucent = false;
    }

    SetVar(repObj, ISTRANSLUCENT, NewInt(isTranslucent));
    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr Change2Sided(object)
ObjPtr object;
/*Changed value for a 2-sided checkbox*/
{
    int is2Sided;
    ObjPtr val;
    ObjPtr repObj;

    repObj = GetObjectVar("Change2Sided", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetVar(object, VALUE);
    if (val)
    {
	is2Sided = GetInt(val);
    }
    else
    {
	is2Sided = false;
    }

    SetVar(repObj, TWOSIDEDSURFACE, NewInt(is2Sided));
    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeBaseColor(object)
ObjPtr object;
/*Change material base color in response to an HS color manipulation*/
{
    ObjPtr valueArray;
    real value[2];
    ObjPtr rgbArray;
    ObjPtr repObj;
    ObjPtr colors;
    ObjPtr sw;
    real rgb[3];

#ifdef INTERACTIVE
    interactiveMoving = false;
#endif
    repObj = GetObjectVar("ChangeBaseColor", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    /*Get the current value of the control*/
    valueArray = GetFixedArrayVar("ChangeBaseColor", object, VALUE, 1, 2L);
    if (!valueArray)
    { 
	return ObjFalse;
    }
    Array2CArray(value, valueArray);

    HSV2RGB(&(rgb[0]), &(rgb[1]), &(rgb[2]), value[0], value[1], 1.0);

    rgbArray = NewRealArray(1, 3L);
    CArray2Array(rgbArray, rgb);

#ifdef DIKEO
printf("%d %d %d\n",
	(int) (rgb[0] * 255.0),
	(int) (rgb[1] * 255.0),
	(int) (rgb[2] * 255.0));
#endif

    SetVar(repObj, BASECOLOR, rgbArray);

    /*Set the switch to be me*/
    sw = GetVar(object, OTHERSWITCH);
    if (sw)
    {
	SetValue(sw, NewInt(0));
    }

    /*Draw me, because we may be tracking*/
    DrawMe(repObj);
    return ObjTrue;
}

static ObjPtr ChangeHighlightColor(object)
ObjPtr object;
/*Change material highlight color in response to an HS color manipulation*/
{
    ObjPtr valueArray;
    real value[2];
    ObjPtr rgbArray;
    ObjPtr repObj;
    ObjPtr colors;
    real rgb[3];

#ifdef INTERACTIVE
    interactiveMoving = false;
#endif
    repObj = GetObjectVar("ChangeHighlightColor", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    /*Get the current value of the control*/
    valueArray = GetFixedArrayVar("ChangeHighlightColor", object, VALUE, 1, 2L);
    if (!valueArray)
    { 
	return ObjFalse;
    }
    Array2CArray(value, valueArray);

    HSV2RGB(&(rgb[0]), &(rgb[1]), &(rgb[2]), value[0], value[1], 1.0);

    rgbArray = NewRealArray(1, 3L);
    CArray2Array(rgbArray, rgb);

    SetVar(repObj, HIGHLIGHTCOLOR, rgbArray);

    /*Draw me, because we may be tracking*/
    DrawMe(repObj);
    return ObjTrue;
}

ObjPtr ChangeControlPanelButton(object)
ObjPtr object;
/*Changedvalue for a control button*/
{
    ObjPtr panel, panelContents, value, curButton, parent;

    value = GetValue(object);

    parent = GetVar(object, PARENT);
    curButton = GetVar(parent, BUTTON);

    if (value && IsInt(value) && 0 == GetInt(value) && curButton != object)
    {
	panel = GetObjectVar("ChangeControlPanelButton", object, PANEL);
	panelContents = GetListVar("ChangeControlPanelButton", object, PANELCONTENTS);
	if (panel && panelContents)
	{
	    ThingListPtr runner;
	    runner = LISTOF(panelContents);
	    while (runner)
	    {
		SetVar(runner -> thing, PARENT, panel);
		runner = runner -> next;
	    }
	    SetVar(panel, CONTENTS, panelContents);
	    SetVar(curButton, VALUE, NewInt(0));
	    SetVar(object, VALUE, NewInt(1));
	    SetVar(parent, BUTTON, object);
	    ImInvalid(panel);
	    return ObjTrue;
	}
	else
	{
	    return ObjFalse;
	}
    }
    else
    {
	return ObjTrue;
    }
}



static ObjPtr ShowVisControls(object, windowName)
ObjPtr object;
char *windowName;
/*Makes a new control window to control visualization object*/
{
    WinInfoPtr controlWindow;
    ObjPtr var;
    ObjPtr panel;
    ObjPtr controlField;
    ObjPtr contents;
    ObjPtr curObj;
    ObjPtr firstButton = NULLOBJ;
    int left, right, bottom, top, width;
    WinInfoPtr dialogExists;
    Bool abortp = false;

    dialogExists = DialogExists((WinInfoPtr) object, NewString("Controls"));
    controlWindow = GetDialog((WinInfoPtr) object, NewString("Controls"), windowName, 
	CWINWIDTH, CWINHEIGHT, CWINWIDTH, CWINHEIGHT, WINFIXEDSIZE + WINDBUF);
    
    if (!dialogExists)
    {
	SetVar((ObjPtr) controlWindow, REPOBJ, object);

	/*Add in help string*/
	SetVar((ObjPtr) controlWindow, HELPSTRING,
	    NewString("This window shows controls for a visualization object.  \
At the right is an icon corral showing a series of icons.  Each icon represents a \
set of attributes of the visualization object.  On the left are the controls for \
the selected set of attributes.  \
Use Help In Context and click on the various controls to find out what they do.  \
Click on a different icon to choose a different set of attributes."));

	/*Add in a panel*/
	panel = NewPanel(greyPanelClass, 0, CWINWIDTH, 0, CWINHEIGHT);
	if (!panel)
	{
	    return ObjFalse;
	}
	contents = GetVar((ObjPtr) controlWindow, CONTENTS);
	PrefixList(contents, panel);
	SetVar(panel, PARENT, (ObjPtr) controlWindow);
	ContentsExpectWindowSize(controlWindow, CWINWIDTH, CWINHEIGHT);

	contents = GetVar(panel, CONTENTS);

	/*Add in a control field*/
	controlField = NewControlField(CWINWIDTH - CORRALBORDER - CWINCORRALWIDTH,
			       CWINWIDTH - CORRALBORDER,
			       CORRALBORDER,
			       CWINHEIGHT - CORRALBORDER,
				"Visualization Object Attributes", OBJECTSFROMTOP | BARRIGHT);
	SetVar(controlField, HELPSTRING,
	   NewString("This icon button group shows sets of attributes of the visualization \
object that can be modified.  The left side of the panel shows controls for the \
attribute given by the selected icon button.  To show another set of \
attributes, press another button."));
	SetVar(controlField, PARENT, panel);
	PrefixList(contents, controlField);

	contents = GetVar(controlField, CONTENTS);

	/*Fill the control field up with buttons*/
	curObj = object;
	top = -MAJORBORDER;
	width = CWINCCWIDTH;
	while (curObj)
	{
	    ObjPtr icon;
	    icon = Get1Var(curObj, CONTROLICON);
	    if (icon)
	    {
		ObjPtr button;
		ObjPtr panelContents;
		FuncTyp method;
		int whichIcon;
		char *name;

		if (firstButton && abortp) break;

		var = GetIntVar("ShowVisControls", icon, WHICHICON);
		if (var)
		{
		    whichIcon = GetInt(var);
		}
		else
		{
		    whichIcon = ICONQUESTION;
		}

		var = GetStringVar("ShowVisControls", icon, NAME);
		if (var)
		{
		    name = GetString(var);
		}
		else
		{
		    name = "Unknown";
		}

		button = NewIconLabeledButton(0, width, top - CWINICONBUTHEIGHT, top,
			whichIcon, UIYELLOW, name, BS_PITTED);
		SetMethod(button, ICONEXTRADRAW, GetMethod(icon, ICONEXTRADRAW));

		SetMethod(button, CHANGEDVALUE, ChangeControlPanelButton);

		SetVar(button, PANEL, panel);

		/*Make a new panel contents just for this button*/
		panelContents = NewList();
		PrefixList(panelContents, controlField);
		SetVar(panelContents, PARENT, panel);
		SetVar(button, PANELCONTENTS, panelContents);
		SetVar(button, PARENT, panelContents);

		/*Give the button a chance to add controls*/
		method = Get1Method(curObj, ADDCONTROLS);
		if (method)
		{
		    (*method)(object, panelContents);
		}
		PrefixList(contents, button);
		SetVar(button, PARENT, controlField);
		top -= CWINICONBUTHEIGHT + MINORBORDER;
    
		if (!firstButton)
		{
		    ObjPtr mainDataset;
    
		    firstButton = button;
		    /*Give the MAINDATASET a chance to set controls*/
		    mainDataset = GetVar(object, MAINDATASET);
		    while (mainDataset)
		    {
			icon = GetVar(mainDataset, CONTROLICON);
			if (icon)
			{
			    ObjPtr panelContents;
			    FuncTyp method;
    
			    var = GetIntVar("ShowVisControls", icon, WHICHICON);
			    if (var)
			    {
				whichIcon = GetInt(var);
			    }
			    else
			    {
				whichIcon = ICONQUESTION;
			    }
    
			    var = GetStringVar("ShowVisControls", icon, NAME);
			    if (var)
			    {
				name = GetString(var);
			    }
			    else
			    {
				name = "Unknown";
			    }
		    
			    button = NewIconLabeledButton(0, width, top - CWINICONBUTHEIGHT, top,
				    whichIcon, UIYELLOW, name, BS_PITTED);
			    SetMethod(button, ICONEXTRADRAW, GetMethod(icon, ICONEXTRADRAW));
    
			    SetMethod(button, CHANGEDVALUE, ChangeControlPanelButton);
    
			    SetVar(button, PANEL, panel);
    
			    /*Make a new panel contents just for this button*/
			    panelContents = NewList();
			    PrefixList(panelContents, controlField);
			    SetVar(panelContents, PARENT, panel);
			    SetVar(button, PANELCONTENTS, panelContents);
			    SetVar(button, PARENT, panelContents);
    
			    /*Give the button a chance to add controls*/
			    method = GetMethod(mainDataset, ADDCONTROLS);
			    if (method)
			    {
				(*method)(mainDataset, panelContents);
			    }
			    PrefixList(contents, button);
			    SetVar(button, PARENT, controlField);
			    top -= CWINICONBUTHEIGHT + MINORBORDER;
			}
			mainDataset = GetVar(mainDataset, MAINDATASET);
		    }
		}
	    }
	    if (GetPredicate(curObj, ABORTCONTROLS)) abortp = true;
	    curObj = ClassOf(curObj);
	}

	/*Adjust the scroll bars*/
	RecalcScroll(controlField);

	if (firstButton)
	{
	    SetValue(firstButton, NewInt(1));
	    SetVar(controlField, BUTTON, firstButton);
	}
    }

    return (ObjPtr) controlWindow;
}

#define NBBCHECKBOXES 6			/*Number of BB check boxes*/

char *bbCheckNames[NBBCHECKBOXES] =
    {
	"Inner Corner",
	"Floor",
	"Ceiling",
	"Outline Box",
	"Thick Lines",
	"Axis Names"
    };

char *bbCheckHelp[NBBCHECKBOXES] =
    {
	"If this box is checked, the inner corner of the bounding box of the visualization object will be shown in gray.",
	"If this box is checked, the floor of the bounding box of the visualization object will be shown in gray.",
	"If this box is checked, the ceiling of the bounding box of the visualization object will be shown in gray.",
	"If this box is checked, a wire frame outline will be drawn around the bounding box of the visualization object.",
	"If this box is checked, thicker lines will be used to draw the bounding box and tic marks.",
	"If this box is checked, the names of the axes will be shown.",
    };

static ObjPtr ChangeBoundsMin(box)
ObjPtr box;
/*Changes bounds minimum*/
{
    ObjPtr repObj;
    ObjPtr value;
    real min;
    ObjPtr var;
    real *elements;
    int k;
    
    repObj = GetObjectVar("ChangeBoundsMin", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(box);
    if (!ParseReal(&min, GetString(value)))
    {
	return ObjFalse;
    }

    if (min == missingData)
    {
	DoUniqueTask(DoMissingError);
	return ObjFalse;
    }
    if (min >= plusInf || min <= minusInf)
    {
	DoUniqueTask(DoInfinityError);
	return ObjFalse;
    }

    var = GetFixedArrayVar("ChangeBoundsMin", repObj, BOUNDS, 1, 6L);
    if (!var)
    {
	return ObjFalse;
    }
    elements = ELEMENTS(var);

    var = GetIntVar("ChangeBoundsMin", box, WHICHDIMENSION);
    if (!var)
    {
	return ObjFalse;
    }
    k = GetInt(var);

    if (min > elements[2 * k + 1])
    {
	DoUniqueTask(DoRangeError);
	return ObjFalse;
    }

    var = NewRealArray(1, 6L);
    CArray2Array(var, elements);
    elements = ELEMENTS(var);
    elements[2 * k] = min;
    SetVar(repObj, BOUNDS, var);
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeBoundsMax(box)
ObjPtr box;
/*Changes bounds minimum*/
{
    ObjPtr repObj;
    ObjPtr value;
    real max;
    ObjPtr var;
    real *elements;
    int k;
    
    repObj = GetObjectVar("ChangeBoundsMax", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(box);
    if (!ParseReal(&max, GetString(value)))
    {
	return ObjFalse;
    }
    if (max == missingData)
    {
	DoUniqueTask(DoMissingError);
	return ObjFalse;
    }
    if (max >= plusInf || max <= minusInf)
    {
	DoUniqueTask(DoInfinityError);
	return ObjFalse;
    }

    var = GetFixedArrayVar("ChangeBoundsMax", repObj, BOUNDS, 1, 6L);
    if (!var)
    {
	return ObjFalse;
    }
    elements = ELEMENTS(var);

    var = GetIntVar("ChangeBoundsMax", box, WHICHDIMENSION);
    if (!var)
    {
	return ObjFalse;
    }
    k = GetInt(var);

    if (max < elements[2 * k])
    {
	DoUniqueTask(DoRangeError);
	return ObjFalse;
    }

    var = NewRealArray(1, 6L);
    CArray2Array(var, elements);
    elements = ELEMENTS(var);
    elements[2 * k + 1] = max;
    SetVar(repObj, BOUNDS, var);
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeTicDensity(box)
ObjPtr box;
/*Changes tic density*/
{
    ObjPtr repObj;
    ObjPtr value;
    ObjPtr var;
    real td;
    real *elements;
    int k;
    
    repObj = GetObjectVar("ChangeTicDensity", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(box);
    if (!ParseReal(&td, GetString(value)))
    {
	return ObjFalse;
    }
    if (td == missingData)
    {
	DoUniqueTask(DoMissingError);
	return ObjFalse;
    }
    if (td >= plusInf || td <= minusInf)
    {
	DoUniqueTask(DoInfinityError);
	return ObjFalse;
    }

    var = GetFixedArrayVar("ChangeTicDensity", repObj, TICDENSITY, 1, 3L);
    if (!var)
    {
	return ObjFalse;
    }
    elements = ELEMENTS(var);

    var = GetIntVar("ChangeTicDensity", box, WHICHDIMENSION);
    if (!var)
    {
	return ObjFalse;
    }
    k = GetInt(var);

    var = NewRealArray(1, 3L);
    CArray2Array(var, elements);
    elements = ELEMENTS(var);
    elements[k] = td;
    SetVar(repObj, TICDENSITY, var);
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeTicLength(box)
ObjPtr box;
/*Changes tic density*/
{
    ObjPtr repObj;
    ObjPtr value;
    ObjPtr var;
    real tl;
    real *elements;
    int k;
    
    repObj = GetObjectVar("ChangeTicLength", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(box);
    if (!ParseReal(&tl, GetString(value)))
    {
	return ObjFalse;
    }
    if (tl == missingData)
    {
	DoUniqueTask(DoMissingError);
	return ObjFalse;
    }
    if (tl >= plusInf || tl <= minusInf)
    {
	DoUniqueTask(DoInfinityError);
	return ObjFalse;
    }

    var = GetFixedArrayVar("ChangeTicLength", repObj, TICLENGTH, 1, 3L);
    if (!var)
    {
	return ObjFalse;
    }
    elements = ELEMENTS(var);

    var = GetIntVar("ChangeTicLength", box, WHICHDIMENSION);
    if (!var)
    {
	return ObjFalse;
    }
    k = GetInt(var);

    var = NewRealArray(1, 3L);
    CArray2Array(var, elements);
    elements = ELEMENTS(var);
    elements[k] = tl;
    SetVar(repObj, TICLENGTH, var);
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeXScale(box)
ObjPtr box;
/*Changes X scaling*/
{
    ObjPtr repObj;
    ObjPtr value;
    ObjPtr var;
    real scale;
    real *elements;
    int k;
    
    repObj = GetObjectVar("ChangeXScale", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(box);
    if (!ParseReal(&scale, GetString(value)))
    {
	return ObjFalse;
    }
    if (scale == missingData)
    {
	DoUniqueTask(DoMissingError);
	return ObjFalse;
    }
    if (scale >= plusInf || scale <= minusInf)
    {
	DoUniqueTask(DoInfinityError);
	return ObjFalse;
    }

    SetVar(repObj, XSCALE, NewReal(scale));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeYScale(box)
ObjPtr box;
/*Changes Y scaling*/
{
    ObjPtr repObj;
    ObjPtr value;
    ObjPtr var;
    real scale;
    real *elements;
    int k;
    
    repObj = GetObjectVar("ChangeYScale", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(box);
    if (!ParseReal(&scale, GetString(value)))
    {
	return ObjFalse;
    }
    if (scale == missingData)
    {
	DoUniqueTask(DoMissingError);
	return ObjFalse;
    }
    if (scale >= plusInf || scale <= minusInf)
    {
	DoUniqueTask(DoInfinityError);
	return ObjFalse;
    }

    SetVar(repObj, YSCALE, NewReal(scale));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeZScale(box)
ObjPtr box;
/*Changes Z scaling*/
{
    ObjPtr repObj;
    ObjPtr value;
    ObjPtr var;
    real scale;
    real *elements;
    int k;
    
    repObj = GetObjectVar("ChangeZScale", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(box);
    if (!ParseReal(&scale, GetString(value)))
    {
	return ObjFalse;
    }
    if (scale == missingData)
    {
	DoUniqueTask(DoMissingError);
	return ObjFalse;
    }
    if (scale >= plusInf || scale <= minusInf)
    {
	DoUniqueTask(DoInfinityError);
	return ObjFalse;
    }

    SetVar(repObj, ZSCALE, NewReal(scale));
    ImInvalid(repObj);

    return ObjTrue;
}


static ObjPtr ChangeXName(box)
ObjPtr box;
/*Changes the X axis name based on box*/
{
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeXName", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, XNAME, GetValue(box));
    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeYName(box)
ObjPtr box;
/*Changes the Y axis name based on box*/
{
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeYName", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, YNAME, GetValue(box));
    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeZName(box)
ObjPtr box;
/*Changes the Z axis name based on box*/
{
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeZName", box, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, ZNAME, GetValue(box));
    ImInvalid(repObj);
    return ObjTrue;
}


static ObjPtr AddBoundedControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls for a visualization object with a bounding box*/
{
    int width;
    int left, top, right, mid, bottom, rightWidth;
    ObjPtr checkBox, titleBox, textBox;
    ObjPtr flagsObj, var;
    int flags;
    int curFlag;
    int i, j, k;
    int tbh;
    real bounds[6];
    real ticDensity[3], ticLength[3];

    width = CWINWIDTH - 2 * CORRALBORDER - CWINCORRALWIDTH;
    left = MINORBORDER;
    top = CWINHEIGHT - MAJORBORDER;

    flagsObj = GetVar(object, BBFLAGS);
    if (flagsObj)
    {
	flags = GetInt(flagsObj);
    }
    else
    {
	flags = 0;
    }

    /*Set up the check boxes*/
    curFlag = 1;
    k = 0;

    for (j = 0; ; ++j)
    {
	top = CWINHEIGHT - MINORBORDER - j * (CHECKBOXHEIGHT + CHECKBOXSPACING);
	bottom = top - CHECKBOXHEIGHT;

	for (i = 0; i < 3; ++i)
	{
	    left = MAJORBORDER + i * (width - MAJORBORDER) / 3;
	    right = left + (width - MAJORBORDER) / 3;

	    /*Create the check box*/
	    checkBox = NewCheckBox(left, right, bottom, top, 
				bbCheckNames[k], flags & curFlag ? 1 : 0);
	    if (!checkBox)
	    {
		return ObjFalse;
	    }
            PrefixList(panelContents, checkBox);
	    SetVar(checkBox, HELPSTRING, NewString(bbCheckHelp[k]));
	    SetVar(checkBox, PARENT, panelContents);
	    SetVar(checkBox, WHICHFLAG, NewInt(curFlag));
	    SetVar(checkBox, REPOBJ, object);
    	    SetMethod(checkBox, CHANGEDVALUE, ChangeBBFlag);
	    curFlag += curFlag;
	    ++k;
	    if (k >= NBBCHECKBOXES)
	    {
		goto noMoreBoxes;
	    }
	}
    }

noMoreBoxes:

    GetBounds(object, bounds);

    var = GetFixedArrayVar("AddBoundedControls", object, TICDENSITY, 1, 3L);
    if (var)
    {
	Array2CArray(ticDensity, var);
    }
    else
    {
	ticDensity[0] = ticDensity[1] = ticDensity[2] = 10.0;
    }

    MakeVar(object, TICLENGTH);
    var = GetFixedArrayVar("AddBoundedControls", object, TICLENGTH, 1, 3L);
    if (var)
    {
	Array2CArray(ticLength, var);
    }
    else
    {
	ticLength[0] = ticLength[1] = ticLength[2] = 0.05;
    }

    /*Make bottom control group, from bottom to top*/
    bottom = MINORBORDER;
    rightWidth = (width - MINORBORDER - SBFUNCTIONNAMEWIDTH);

    /*Minor tic mark check boxes*/
    top = bottom + CHECKBOXHEIGHT;

    curFlag = BBXMINOR;
    for (k = 0; k < 3; ++k)
    {
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;

	checkBox = NewCheckBox(left, right,
			       bottom, 
			       top, 
			       k == 0 ? "Minor" : k == 1 ? "Minor " : "Minor  ",
			       flags & curFlag ? 1 : 0);
	if (!checkBox)
	{
	    return ObjFalse;
	}
        PrefixList(panelContents, checkBox);
	SetVar(checkBox, PARENT, panelContents);
	sprintf(tempStr, "If this box is checked, minor tic marks will be shown along the %c axis.", k == 0 ? 'X' : k == 1 ? 'Y' : 'Z');
	SetVar(checkBox, HELPSTRING, NewString(tempStr));
	SetVar(checkBox, WHICHFLAG, NewInt(curFlag));
	SetVar(checkBox, REPOBJ, object);
    	SetMethod(checkBox, CHANGEDVALUE, ChangeBBFlag);
	curFlag *= 4;
    }
    bottom = top + CHECKBOXSPACING;

    /*Major tic mark check boxes*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    top = bottom + CHECKBOXHEIGHT;

    textBox = NewTextBox(left, right, 
			top - TEXTBOXHEIGHT - MINORBORDER,
			top - MINORBORDER,
			0, "Tic Marks Text", "Tic Marks:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    curFlag = BBXMAJOR;
    for (k = 0; k < 3; ++k)
    {
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;

	checkBox = NewCheckBox(left, right,
			       bottom, 
			       top, 
			       k == 0 ? "Major" : k == 1 ? "Major " : "Major  ",
			       flags & curFlag ? 1 : 0);
	if (!checkBox)
	{
	    return ObjFalse;
	}
        PrefixList(panelContents, checkBox);
	SetVar(checkBox, PARENT, panelContents);
	sprintf(tempStr, "If this box is checked, major tic marks will be shown along the %c axis.", k == 0 ? 'X' : k == 1 ? 'Y' : 'Z');
	SetVar(checkBox, HELPSTRING, NewString(tempStr));
	SetVar(checkBox, WHICHFLAG, NewInt(curFlag));
	SetVar(checkBox, REPOBJ, object);
    	SetMethod(checkBox, CHANGEDVALUE, ChangeBBFlag);
	curFlag *= 4;
    }
    bottom = top + SBFUNCTIONSPACING;

    /*Tic length controls*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    top = bottom + EDITBOXHEIGHT;
    mid = (bottom + top) / 2;
    textBox = NewTextBox(left, right, 
			mid - TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			mid + TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			0, "Tic Length Text", "Tic Length:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    left = right;

    for (k = 0; k < 3; ++k)
    {
	/*Make the tic length box*/
	PrintNumber(tempStr, ticLength[k]);
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;
	textBox = NewTextBox(left, right,
		mid - EDITBOXHEIGHT / 2,
		mid + EDITBOXHEIGHT / 2,
		EDITABLE + WITH_PIT + ONE_LINE,
		k == 2 ? "Z Tic Length" : (k ? "Y Tic Length" : "X Tic Length"),
		tempStr); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetVar(textBox, WHICHDIMENSION, NewInt(k));
	SetVar(textBox, REPOBJ, object);
	SetTextAlign(textBox, RIGHTALIGN);
	SetMethod(textBox, CHANGEDVALUE, ChangeTicLength);
	SetVar(textBox, HELPSTRING, NewString("This text box contains a number \
which gives the length of tic marks in the indicated dimension.  The number \
is in field coordinates."));
    }
    bottom = top + SBFUNCTIONSPACING;

    /*Tic density controls*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    top = bottom + EDITBOXHEIGHT;
    mid = (bottom + top) / 2;
    textBox = NewTextBox(left, right, 
			mid - TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			mid + TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			0, "Tic Density Text", "Tic Density:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    left = right;

    for (k = 0; k < 3; ++k)
    {
	/*Make the tic density box*/
	PrintNumber(tempStr, ticDensity[k]);
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;
	textBox = NewTextBox(left, right,
		mid - EDITBOXHEIGHT / 2,
		mid + EDITBOXHEIGHT / 2,
		EDITABLE + WITH_PIT + ONE_LINE,
		k == 2 ? "Z Tic Density" : (k ? "Y Tic Density" : "X Tic Density"),
		tempStr); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetVar(textBox, WHICHDIMENSION, NewInt(k));
	SetVar(textBox, REPOBJ, object);
	SetTextAlign(textBox, RIGHTALIGN);
	SetMethod(textBox, CHANGEDVALUE, ChangeTicDensity);
	SetVar(textBox, HELPSTRING, NewString("This text box contains a number \
which gives the density of tic marks in the indicated dimension.  Increase the \
number for more major and minor tics; decrease it for fewer."));
    }
    bottom = top + SBFUNCTIONSPACING;

    /*Axis scaling edit boxes*/
    MakeVar(object, XSCALE);
    MakeVar(object, YSCALE);
    MakeVar(object, ZSCALE);

    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    top = bottom + EDITBOXHEIGHT;
    mid = (bottom + top) / 2;
    textBox = NewTextBox(left, right, 
			mid - TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			mid + TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			0, "Axis Scaling Text", "Scaling:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    left = right;

    for (k = 0; k < 3; ++k)
    {
	/*Make the axis edit box*/
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;

	var = GetRealVar("AddBoundedControls", object, 
		k == 2 ? ZSCALE : (k ? YSCALE : XSCALE));
	if (var)
	{
	    PrintNumber(tempStr, GetReal(var));
	}
	else
	{
	    strcpy(tempStr, "1");
	}
	textBox = NewTextBox(left, right,
		mid - EDITBOXHEIGHT / 2,
		mid + EDITBOXHEIGHT / 2,
		EDITABLE + WITH_PIT + ONE_LINE,
		k == 2 ? "Z Axis Scaling" : (k ? "Y Axis Scaling" : "X Axis Scaling"),
		tempStr); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetVar(textBox, WHICHDIMENSION, NewInt(k));
	SetVar(textBox, REPOBJ, object);
	SetTextAlign(textBox, RIGHTALIGN);
	SetMethod(textBox, CHANGEDVALUE, 
		k == 2 ? ChangeZScale : (k ? ChangeYScale : ChangeXScale));
	SetVar(textBox, HELPSTRING, NewString("This text box contains the scaling \
of the selected axis.  Normally, all three axes are scaled at 1, indicating that \
all three axes have the same units.  However, sometimes it is useful to use different \
scales.\n\nFor example, say you have a mesh of terrain.  The X and Y coordinates are \
in kilometers, but the Z coordinate is in meters.  To make the visualization appear \
accurately, you might make the X and Y scaling 1000 while keeping the Z scaling at 1."));
    }
    bottom = top + SBFUNCTIONSPACING;

    /*Min edit boxes*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    top = bottom + EDITBOXHEIGHT;
    mid = (bottom + top) / 2;
    textBox = NewTextBox(left, right, 
			mid - TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			mid + TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			0, "Minimum Text", "Minimum:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    left = right;

    for (k = 0; k < 3; ++k)
    {
	/*Make the minimum edit box*/
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;

	PrintNumber(tempStr, bounds[k * 2]);
	textBox = NewTextBox(left, right,
		mid - EDITBOXHEIGHT / 2,
		mid + EDITBOXHEIGHT / 2,
		EDITABLE + WITH_PIT + ONE_LINE,
		k == 2 ? "Z Minimum" : (k ? "Y Minimum" : "X Minimum"),
		tempStr); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, WHICHDIMENSION, NewInt(k));
	SetVar(textBox, PARENT, panelContents);
	SetVar(textBox, REPOBJ, object);
	SetTextAlign(textBox, RIGHTALIGN);
	SetMethod(textBox, CHANGEDVALUE, ChangeBoundsMin);
	SetVar(textBox, HELPSTRING, NewString("This text box contains a number \
which gives the minimum value of the bounds in the indicated dimension.  Change \
this number to change the bounds.  The number must not be greater than the \
maximum."));
    }
    bottom = top + SBFUNCTIONSPACING;

    /*Max edit boxes*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    top = bottom + EDITBOXHEIGHT;
    mid = (bottom + top) / 2;
    textBox = NewTextBox(left, right, 
			mid - TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			mid + TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			0, "Maximum Text", "Maximum:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    left = right;

    for (k = 0; k < 3; ++k)
    {
	/*Make the maximum edit box*/
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;

	PrintNumber(tempStr, bounds[k * 2 + 1]);
	textBox = NewTextBox(left, right,
		mid - EDITBOXHEIGHT / 2,
		mid + EDITBOXHEIGHT / 2,
		EDITABLE + WITH_PIT + ONE_LINE,
		k == 2 ? "Z Maximum" : (k ? "Y Maximum" : "X Maximum"),
		tempStr); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetVar(textBox, WHICHDIMENSION, NewInt(k));
	SetVar(textBox, REPOBJ, object);
	SetTextAlign(textBox, RIGHTALIGN);
	SetMethod(textBox, CHANGEDVALUE, ChangeBoundsMax);
	SetVar(textBox, HELPSTRING, NewString("This text box contains a number \
which gives the maximum value of the bounds in the indicated dimension.  Change \
this number to change the bounds.  The number must not be less than the \
minimum."));
    }
    bottom = top + SBFUNCTIONSPACING;

    /*Axis name edit boxes*/
    MakeVar(object, XNAME);
    MakeVar(object, YNAME);
    MakeVar(object, ZNAME);

    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    top = bottom + EDITBOXHEIGHT;
    mid = (bottom + top) / 2;
    textBox = NewTextBox(left, right, 
			mid - TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			mid + TEXTBOXHEIGHT / 2 + EDITBOXDOWN,
			0, "Axis Name Text", "Axis Name:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    left = right;

    for (k = 0; k < 3; ++k)
    {
	/*Make the axis edit box*/
	left = 2 * MINORBORDER + SBFUNCTIONNAMEWIDTH + k * rightWidth / 3;
	right = left + rightWidth / 3 - MINORBORDER;

	var = GetStringVar("AddBoundedControls", object, 
		k == 2 ? ZNAME : (k ? YNAME : XNAME));
	if (var)
	{
	    strcpy(tempStr, GetString(var));
	}
	else
	{
	    strcpy(tempStr, k == 2 ? "Z" : (k ? "Y" : "X"));
	}
	textBox = NewTextBox(left, right,
		mid - EDITBOXHEIGHT / 2,
		mid + EDITBOXHEIGHT / 2,
		EDITABLE + WITH_PIT + ONE_LINE,
		k == 2 ? "Z Axis Name" : (k ? "Y Axis Name" : "X Axis Name"),
		tempStr); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetVar(textBox, WHICHDIMENSION, NewInt(k));
	SetVar(textBox, REPOBJ, object);
	SetTextAlign(textBox, RIGHTALIGN);
	SetMethod(textBox, CHANGEDVALUE, 
		k == 2 ? ChangeZName : (k ? ChangeYName : ChangeXName));
	SetVar(textBox, HELPSTRING, NewString("This text box contains the name of the \
indicated axis.  This is the name displayed when Axis Names box is checked."));
    }

    return ObjTrue;
}

static ObjPtr ChangeCSValue(object)
ObjPtr object;
/*Changes the value of color switch*/
{
    ObjPtr var;
    int value;
    ObjPtr repObj;

    /*Get the value for comparison*/
    var = GetIntVar("ChangeCSValue", object, VALUE);
    if (!var) return ObjFalse;
    value = GetInt(var);

    /*Get the repobj*/
    repObj = GetObjectVar("ChangeCSValue", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    if (value)
    {
	/*Switch to colored by object*/
	SetVar(repObj, COLORS, NewInt(1));
    }
    else
    {
	/*Switch to null color*/
	SetVar(repObj, COLORS, NULLOBJ);
    }

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeDSValue(object)
ObjPtr object;
/*Changes the value of deformation switch*/
{
    ObjPtr var;
    int value;
    ObjPtr repObj;

    /*Get the value for comparison*/
    var = GetIntVar("ChangeDSValue", object, VALUE);
    if (!var) return ObjFalse;
    value = GetInt(var);

    /*Get the repobj*/
    repObj = GetObjectVar("ChangeDSValue", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    if (value)
    {
	/*Switch to colored by object*/
	SetVar(repObj, DEFORMSWITCH, NewInt(1));
    }
    else
    {
	/*Switch to null color*/
	SetVar(repObj, DEFORMSWITCH, NewInt(0));
    }

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr DropInColorCorral(corral, object, x, y)
ObjPtr corral, object;
int x, y;
/*Drops an icon in a field corral*/
{
    ObjPtr visObj;
    ObjPtr fieldObj;
    ObjPtr icon;
    ObjPtr name;
    ObjPtr defaultIcon;
    ObjPtr contents;
    ObjPtr button;
    long info;

    /*Find the visualization object*/
    visObj = GetObjectVar("DropInColorCorral", corral, REPOBJ);
    if (!visObj)
    {
	return ObjFalse;
    }

    /*Get the field object*/
    fieldObj = GetObjectVar("DropInColorCorral", object, REPOBJ);
    if (!fieldObj)
    {
	return ObjFalse;
    }

    info = GetDatasetInfo(fieldObj);
    if ((info & DS_HASGEOMETRY) && (fieldObj != GetVar(visObj, MAINDATASET)))
    {
	DoTask(CannotColorByGeometry);
	return ObjFalse;
    }

    /*Create an icon for it*/
    name = GetStringVar("DropInColorCorral", fieldObj, NAME);
    if (!name)
    {
	return ObjFalse;
    }

    defaultIcon = GetVar(fieldObj, DEFAULTICON);
    if (defaultIcon)
    {
	ObjPtr locArray;
	real loc[2];
	icon = NewObject(defaultIcon, 0);
	SetVar(icon, NAME, name);
	loc[0] = x;
	loc[1] = y;
	locArray = NewRealArray(1, 2L);
	CArray2Array(locArray, loc);
	SetVar(icon, ICONLOC, locArray);
    }
    else
    {
	icon = NewIcon(x, y, ICONQUESTION, GetString(name));
    }
    
    /*Make the icon point to the field*/
    SetVar(icon, REPOBJ, fieldObj);

    /*Make it the only icon in the corral*/
    contents = NewList();
    PrefixList(contents, icon);
    SetVar(corral, CONTENTS, contents);
    SetVar(contents, PARENT, corral);
    SetVar(icon, PARENT, corral);
    RecalcScroll(corral);

    /*Make this object the colored object*/
    SetVar(visObj, COLOROBJ, fieldObj);

    /*Activate the show palette button*/
    button = GetObjectVar("DropInColorCorral", corral, BUTTON);
    if (button)
    {
	ActivateButton(button, true);
    }

    ImInvalid(visObj);
    ImInvalid(corral);
    return ObjTrue;
}

static ObjPtr DropInDeformCorral(corral, object, x, y)
ObjPtr corral, object;
int x, y;
/*Drops an icon in a deformation corral*/
{
    ObjPtr visObj;
    ObjPtr fieldObj;
    ObjPtr icon;
    ObjPtr name;
    ObjPtr defaultIcon;
    ObjPtr contents;
    ObjPtr button;
    long info;

    /*Find the visualization object*/
    visObj = GetObjectVar("DropInDeformCorral", corral, REPOBJ);
    if (!visObj)
    {
	return ObjFalse;
    }

    /*Get the field object*/
    fieldObj = GetObjectVar("DropInDeformCorral", object, REPOBJ);
    if (!fieldObj)
    {
	return ObjFalse;
    }

    info = GetDatasetInfo(fieldObj);
    if ((info & DS_HASGEOMETRY))
    {
	DoTask(CannotDeformByGeometry);
	return ObjFalse;
    }

    /*Create an icon for it*/
    name = GetStringVar("DropInDeformCorral", fieldObj, NAME);
    if (!name)
    {
	return ObjFalse;
    }

    defaultIcon = GetVar(fieldObj, DEFAULTICON);
    if (defaultIcon)
    {
	ObjPtr locArray;
	real loc[2];
	icon = NewObject(defaultIcon, 0);
	SetVar(icon, NAME, name);
	loc[0] = x;
	loc[1] = y;
	locArray = NewRealArray(1, 2L);
	CArray2Array(locArray, loc);
	SetVar(icon, ICONLOC, locArray);
    }
    else
    {
	icon = NewIcon(x, y, ICONQUESTION, GetString(name));
    }
    
    /*Make the icon point to the field*/
    SetVar(icon, REPOBJ, fieldObj);

    /*Make it the only icon in the corral*/
    contents = NewList();
    PrefixList(contents, icon);
    SetVar(corral, CONTENTS, contents);
    SetVar(contents, PARENT, corral);
    SetVar(icon, PARENT, corral);
    RecalcScroll(corral);

    /*Make this object the deformed object*/
    SetVar(visObj, DEFORMOBJ, fieldObj);

    ImInvalid(visObj);
    ImInvalid(corral);
    return ObjTrue;
}

static ObjPtr globalPalette = 0;

static void ShowPaletteFromButton()
/*Shows a palette as a result of a button press*/
{
    if (globalPalette)
    {
	NewControlWindow(globalPalette);
    }
    globalPalette = 0;
}

ObjPtr ChangeShowPalette(object)
ObjPtr object;
/*Shows the palette of an object*/
{
    ObjPtr repObj, palette, contents, icon;
    ThingListPtr list;

    repObj = GetObjectVar("ChangeShowPalette", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    contents = GetListVar("ChangeShowPalette", repObj, CONTENTS);
    if (!contents)
    {
	return ObjFalse;
    }

    list = LISTOF(contents);
    if (!list)
    {
	ActivateButton(object, false);
	return ObjFalse;
    }

    icon = list -> thing;
    if (!icon)
    {
	return ObjFalse;
    }

    repObj = GetObjectVar("ChangeShowPalette", icon, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    palette = GetPaletteVar("ChangeShowPalette", repObj, CPALETTE);
    if (!palette)
    {
	return ObjFalse;
    }

    globalPalette = palette;
    DoUniqueTask(ShowPaletteFromButton);
    return ObjTrue;
}

static ObjPtr ChangeColorShading(object)
ObjPtr object;
/*Change and object's color shading variable based on a radio group*/
{
    ObjPtr val;
    ObjPtr repObj;
    int shadeSmooth;

    repObj = GetObjectVar("ChangeColorShading", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(object);
    if (val)
    {
	shadeSmooth = GetInt(val);
    }
    else
    {
	shadeSmooth = 0;
    }

    SetVar(repObj, COLORSHADING, shadeSmooth ? ObjTrue : ObjFalse);

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeInterpSamples(object)
ObjPtr object;
/*Change and object's INTERPCOLORS variable based on a radio group*/
{
    ObjPtr repObj;
    ObjPtr val;
    Bool interp;

    repObj = GetObjectVar("ChangeColorShading", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(object);
    if (val)
    {
	interp = GetInt(val);
    }
    else
    {
	interp = 0;
    }

    SetVar(repObj, INTERPCOLORS, interp ? ObjTrue : ObjFalse);

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr AddColoredControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls appropriate to a colored object to panelContents*/
{
    ObjPtr control, button, checkBox, corral, sw, textBox, slider, icon, radioGroup;
    ObjPtr colorObj, titleBox;
    ObjPtr var;
    int width, left, right, top, cellHeight, m1, m2;
    real initValue;
    real baseColor[3];
    real hs[2], dummy;
    ObjPtr parent;

    /*Get the parent*/
    parent = GetObjectVar("AddColoredControls", panelContents, PARENT);

    width = CWINWIDTH - 2 * CORRALBORDER - CWINCORRALWIDTH;
    left = MAJORBORDER;

    cellHeight = MAX(COLORWHEELWIDTH, ONECORRALHEIGHT) - 10;

    /*Precalculate the midlines for convenience*/
    m1 = CWINHEIGHT - MAJORBORDER - cellHeight / 2;
    m1 += MINORBORDER;
    m2 = m1 - cellHeight - MAJORBORDER - TEXTBOXHEIGHT - TEXTBOXSEP;

    /*Create the color source corral*/
    corral = NewIconCorral(NULLOBJ,
			   left, left + ONECORRALWIDTH,
			   m2 - ONECORRALHEIGHT / 2,
			   m2 + ONECORRALHEIGHT / 2, 0);
    SetVar(corral, SINGLECORRAL, ObjTrue);
    SetVar(corral, TOPDOWN, ObjTrue);
    SetVar(corral, NAME, NewString("Color Field"));
    PrefixList(panelContents, corral);
    SetVar(corral, HELPSTRING,
	NewString("This corral shows the dataset that is used to give \
the visualization object its color, according to the position of the color switch.  \
To replace it with another dataset, drag the icon of the other \
dataset into this corral."));
    SetVar(corral, PARENT, panelContents);
    SetVar(corral, REPOBJ, object);
    SetMethod(corral, DROPINCONTENTS, DropInColorCorral);

    /*Create the color source text box*/
    textBox = NewTextBox(left, left + ONECORRALWIDTH, 
			 m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP - TEXTBOXHEIGHT,
			 m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP,
			 0, "Color Field Text", "Color Field");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    /*Create the show palette button*/
    button = NewButton(left, left + ONECORRALWIDTH,
			m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP - TEXTBOXHEIGHT - MINORBORDER - BUTTONHEIGHT,
			m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP - TEXTBOXHEIGHT - MINORBORDER,
			"Show Palette");
    PrefixList(panelContents, button);
    SetVar(button, PARENT, panelContents);
    SetVar(corral, BUTTON, button);
    SetVar(button, REPOBJ, corral);
    SetMethod(button, CHANGEDVALUE, ChangeShowPalette);
    SetVar(button, HELPSTRING,
	NewString("This button shows the palette of the field in the color \
corral.  First select the icon in the color corral and then press this button.  \
A new window will appear showing the palette controls."));

#if 0
    /*Create the interpolate samples check box*/
    right =  width / 2 + 3 * MAJORBORDER + MINORBORDER;
    checkBox = NewCheckBox(left, right, MAJORBORDER, MAJORBORDER + CHECKBOXHEIGHT,
		"Interpolate Samples", GetPredicate(object, INTERPOLATEP));
    SetVar(checkBox, PARENT, panelContents);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, REPOBJ, object);
    SetMethod(checkBox, CHANGEDVALUE, ChangeInterpSamples);
    SetVar(checkBox, HELPSTRING,
	NewString("This check box controls whether interpolation is done when \
getting samples from the color field.  If the check box is not checked, the colors \
will be derived from the closest sample.  This is fast, but it generates results \
which are not smooth.  If the check box is checked, the color will be interpolated \
between the nearest nodes using linear interpolation.  This is a bit slower, but \
it generates smoother and more accurate results."));
#endif

    left += ONECORRALWIDTH + MINORBORDER;
    colorObj = GetVar(object, COLOROBJ);
    if (colorObj)
    {
	ObjPtr name, defaultIcon;
	/*Drop icon in corral, if need be*/
	name = GetVar(colorObj, NAME);
	defaultIcon = GetVar(colorObj, DEFAULTICON);
	if (defaultIcon)
	{
	    icon = NewObject(defaultIcon, 0);
	    SetVar(icon, NAME, name);
	}
	else
	{
	    icon = NewIcon(0, 0, ICONQUESTION, GetString(name));
        }
	SetVar(icon, REPOBJ, colorObj);
	SetVar(icon, ICONLOC, NULLOBJ);
        DropIconInCorral(corral, icon);
    }
    else
    {
	ActivateButton(button, false);
    }


    /*Create the color control*/
    control = NewColorWheel(left - COLORWHEELWIDTH - MINORBORDER, left - MINORBORDER, 
			m1 - COLORWHEELWIDTH / 2,
			m1 + COLORWHEELWIDTH / 2, "Fixed Color");
    PrefixList(panelContents, control);
    SetVar(control, PARENT, panelContents);
    SetVar(control, REPOBJ, object);
    SetVar(control, HELPSTRING, NewString("This color wheel controls the base color of a \
visualization object.  If the window is in full color mode and the object is light shaded, the \
final color of the object also depends on the lighting."));

    var = GetVar(object, BASECOLOR);
    if (var)
    {
	Array2CArray(baseColor, var);
    }
    else
    {
	baseColor[0] = 1.0;
	baseColor[1] = 1.0;
	baseColor[2] = 1.0;
    }
    RGB2HSV(&(hs[0]), &(hs[1]), &dummy, baseColor[0], baseColor[1], baseColor[2]);
    var = NewRealArray(1, 2L);
    CArray2Array(var, hs);
    SetValue(control, var);
    SetMethod(control, CHANGEDVALUE, ChangeBaseColor);

    /*Create the text box*/
    textBox = NewTextBox(left - COLORWHEELWIDTH - MINORBORDER - 30, left - MINORBORDER + 30, 
			 m1 - cellHeight / 2 - TEXTBOXSEP - TEXTBOXHEIGHT,
			 m1 - cellHeight / 2 - TEXTBOXSEP,
			 0, "Fixed Color Text", "Fixed Color");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    /*Create the choice switch*/
    sw = NewSwitch(left, left + SWITCHWIDTH,
			m2 - cellHeight / 2 - (MAJORBORDER + TEXTBOXHEIGHT + TEXTBOXSEP) / 2,
			m1 + cellHeight / 2 + (MAJORBORDER + TEXTBOXHEIGHT + TEXTBOXSEP) / 2,
			2, 0, GetVar(object, COLORS) ? 1 : 0,
			"Color Switch");
    PrefixList(panelContents, sw);
    SetVar(sw, PARENT, panelContents);
    SetVar(sw, REPOBJ, object);
    SetVar(sw, HELPSTRING, NewString("This switch controls whether the color for the visualization object comes from a \
fixed color or from a field.  The color is passed through a brightness control \
to produce the base color for the object.\n"));
    /*Link it to color wheel*/
    SetVar(control, OTHERSWITCH, sw);


    /*Set change method*/
    SetMethod(sw, CHANGEDVALUE, ChangeCSValue);

    left += SWITCHWIDTH + MINORBORDER;

    /*Create the brightness slider*/
    var = GetVar(object, BRIGHTNESS);
    if (var && IsReal(var))
    {
	initValue = GetReal(var);
    }
    else
    {
	initValue = 1.0;
    }

    slider = NewSlider(left, left + SLIDERWIDTH, 
		       m1 - cellHeight / 2, m1 + cellHeight / 2,
		       PLAIN, "Brightness");
    if (!slider)
    {
	return ObjFalse;
    }
    PrefixList(panelContents, slider);
    SetVar(slider, HELPSTRING, NewString("This slider controls the brightness of \
a visualization object.  Move the indicator up to make the object brighter or down \
to make it dimmer.  If the object is lit, the eventual brightness will also depend \
on the lighting."));
    SetVar(slider, PARENT, panelContents);
    SetSliderRange(slider, 1.0, 0.0, 0.0);
    SetSliderValue(slider, initValue);
    SetVar(slider, REPOBJ, object);
    SetMethod(slider, CHANGEDVALUE, ChangeBrightness);

    /*Create the brightness text box*/
    textBox = NewTextBox(left - MAJORBORDER - MINORBORDER, left + SLIDERWIDTH + MAJORBORDER + MINORBORDER, 
			 m1 - cellHeight / 2 - TEXTBOXSEP - TEXTBOXHEIGHT,
			 m1 - cellHeight / 2 - TEXTBOXSEP,
			 0, "Brightness Text", "Brightness");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    left += SLIDERWIDTH + MAJORBORDER;

    /*Create the unit switch*/
    sw = NewSwitch(left, left + SSWITCHWIDTH,
			   m1 - cellHeight / 2,
			   m1 + cellHeight / 2,
			   1, 0, 0, "Unit Switch");
    PrefixList(panelContents, sw);
    SetVar(sw, PARENT, panelContents);
    SetVar(sw, HELPSTRING, NewString("This switch shows that the color that has passed through \
the brightness control is used to produce the base color of the object.\n"));
    left += SSWITCHWIDTH + MINORBORDER;

    /*Create the color icon*/
    icon = NewIcon(left + MINORBORDER + ICONSIZE / 2, m1,
		   ICONCOLOR, "Base Color");
    PrefixList(panelContents, icon);
    SetVar(icon, PARENT, panelContents);
    SetMethod(icon, PRESS, (FuncTyp) 0);

    left = width - MAJORBORDER - TRANSPARENTWIDTH;

    /*Create the translucent check box, or use the existing one*/
    if (checkBox = GetVar(parent, TRANSLUCENTCNT))
    {
	PrefixList(panelContents, checkBox);
    }
    else
    {
	checkBox = NewCheckBox(left, width, 
		MAJORBORDER + CHECKBOXSPACING + CHECKBOXHEIGHT,
		MAJORBORDER + CHECKBOXSPACING + 2 * CHECKBOXHEIGHT, "Translucent", GetPredicate(object, ISTRANSLUCENT));
	if (!checkBox)
	{
	    return ObjFalse;
	}
	PrefixList(panelContents, checkBox);
	SetVar(checkBox, HELPSTRING,
	    NewString("If this box is checked, the visualization object will be drawn using screen door translucency."));
	SetVar(checkBox, PARENT, parent);
	SetVar(checkBox, REPOBJ, object);
	SetMethod(checkBox, CHANGEDVALUE, ChangeTranslucency);
	SetVar(parent, TRANSLUCENTCNT, checkBox);
    }

    /*Create the transparent check box, or use the existing one*/
    if (checkBox = GetVar(parent, TRANSPARENTCNT))
    {
	PrefixList(panelContents, checkBox);
    }
    else
    {
	checkBox = NewCheckBox(left, width, 
		MAJORBORDER,
		MAJORBORDER + CHECKBOXHEIGHT, "Transparent", GetPredicate(object, ISTRANSPARENT));
	if (!checkBox)
	{
	    return ObjFalse;
	}
	PrefixList(panelContents, checkBox);
	SetVar(checkBox, HELPSTRING,
	    NewString("If this box is checked, the visualization object will be drawn using alpha transparency.  \
It may be useful to turn down the brightness of the object when using this feature.  Transparency \
only works on hardware that supports blending transparency.  If you don't \
have this hardware, try translucency instead."));
	SetVar(checkBox, PARENT, parent);
	SetVar(checkBox, REPOBJ, object);
	SetMethod(checkBox, CHANGEDVALUE, ChangeTransparency);
	SetVar(parent, TRANSPARENTCNT, checkBox);

#ifdef GRAPHICS
	if (!hasTransparency) ActivateButton(checkBox, false);
#endif
    }

    /*Create radio button group for color shading*/
    right = width - MAJORBORDER;
    top = MAJORBORDER + CHECKBOXHEIGHT * 4 + CHECKBOXSPACING * 2 + MAJORBORDER + MINORBORDER;

    radioGroup = NewRadioButtonGroup("Color Shading Radio");

    SetVar(radioGroup, HELPSTRING,
	NewString("These radio buttons allow you to change the way a visualization \
object is shaded with color.  This interacts in interesting ways with the light shading.  \
Experiment with different combinations."));

    /*Title box around it*/
    titleBox = NewTitleBox(left - MINORBORDER, right + MINORBORDER,
		top - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING - MINORBORDER,
		top + CHECKBOXSPACING + TITLEBOXTOP + MINORBORDER,
		"Color Shading");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    checkBox = NewRadioButton(left, right, top - CHECKBOXHEIGHT, top,
		"Flat");
    SetVar(checkBox, HELPSTRING,
	NewString("This button specifies that the visualization object will \
be flatly shaded with color.  This only has a visual effect when the object \
is color shaded by another field.  It may be faster than smooth shading on \
some systems.  The combination of flat light and flat color shading may be even faster."));
    AddRadioButton(radioGroup, checkBox);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    checkBox = NewRadioButton(left, right, top - CHECKBOXHEIGHT, top,
		"Smooth");
    SetVar(checkBox, HELPSTRING,
	NewString("This button specifies that the visualization object will \
be smoothly shaded with color.  This only has a visual effect when the object \
is color shaded with another field.  It may be slower than flat shading on \
some systems."));
    AddRadioButton(radioGroup, checkBox);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    /*Add the radio button group*/
    PrefixList(panelContents, radioGroup);
    SetVar(radioGroup, PARENT, panelContents);

    /*Set its value based on color shading*/
    SetValue(radioGroup, NewInt(GetPredicate(object, COLORSHADING) ? 1 : 0));

    /*Set its REPOBJ and CHANGEDVALUE, now that it's all set*/
    SetVar(radioGroup, REPOBJ, object);
    SetMethod(radioGroup, CHANGEDVALUE, ChangeColorShading);

    return ObjTrue;
}

ObjPtr EnterRealNumber(textBox)
ObjPtr textBox;
/*Gets the value from textBox, converts it to a number, and enter that into
  the repobj's WHICHVAR.  Doesn't allow infinity or missing.*/
{
    ObjPtr repObj, var;
    real value;
    int whichVar;
    
    repObj = GetObjectVar("EnterRealNumber", textBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    var = GetSymbolVar("EnterRealNumber", textBox, WHICHVAR);
    if (!var)
    {
	return ObjFalse;
    }
    whichVar = GetSymbolID(var);

    var = GetValue(textBox);
    if (IsString(var))
    {
	if (!ParseReal(&value, GetString(var)))
	{
	    return ObjFalse;
	}
	if (value <= minusInf || value >= plusInf)
	{
	    DoUniqueTask(DoInfinityError);
	    return ObjFalse;
	}
	if (value == missingData)
	{
	    DoUniqueTask(DoMissingError);
	}
    }
    else if (IsReal(var))
    {
	value = GetReal(var);
    }
    else
    {
	ReportError("EnterRealNumber", "Neither a real number nor a string found");
    }

    SetVar(repObj, whichVar, NewReal(value));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeDeformedReverseSense(button)
ObjPtr button;
/*Changes the sense of the displacement as a result of a change in the value of button*/
{
    ObjPtr repObj;
    ObjPtr value;

    repObj = GetObjectVar("ChangeDeformedReverseSense", button, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(button);
    SetVar(repObj, REVERSESENSE, NewInt(GetInt(value)));
    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr AddDeformedControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls appropriate to a deformed object to panelContents*/
{
    ObjPtr control, button, checkBox, corral, sw, textBox, slider, icon, radioGroup;
    ObjPtr deformObj, titleBox;
    ObjPtr var;
    int width, left, right, top, bottom, cellHeight, m1, m2;
    real initValue;
    real baseColor[3];
    real hs[2], dummy;
    ObjPtr parent;

    /*Get the parent*/
    parent = GetObjectVar("AddDeformedControls", panelContents, PARENT);

    width = CWINWIDTH - 2 * CORRALBORDER - CWINCORRALWIDTH;
    left = MAJORBORDER;

    cellHeight = ONECORRALHEIGHT;

    /*Precalculate the midlines for convenience*/
    m1 = CWINHEIGHT - MAJORBORDER - cellHeight / 2;
    m1 += MINORBORDER;
    m2 = m1 - cellHeight - MAJORBORDER - TEXTBOXHEIGHT - TEXTBOXSEP;

    /*Create the deform source corral*/
    corral = NewIconCorral(NULLOBJ,
			   left, left + ONECORRALWIDTH,
			   m2 - ONECORRALHEIGHT / 2,
			   m2 + ONECORRALHEIGHT / 2, 0);
    SetVar(corral, SINGLECORRAL, ObjTrue);
    SetVar(corral, TOPDOWN, ObjTrue);
    SetVar(corral, NAME, NewString("Deform Field"));
    PrefixList(panelContents, corral);
    SetVar(corral, HELPSTRING,
	NewString("This corral shows the dataset that is used to \
deform the visualization object.  \
To replace it with another dataset, drag the icon of the other \
dataset into this corral."));
    SetVar(corral, PARENT, panelContents);
    SetVar(corral, REPOBJ, object);
    SetMethod(corral, DROPINCONTENTS, DropInDeformCorral);

    /*Create the deform source text box*/
    textBox = NewTextBox(left, left + ONECORRALWIDTH, 
			 m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP - TEXTBOXHEIGHT,
			 m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP,
			 0, "Deform Field Text", "Deform Field");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    left += ONECORRALWIDTH + MINORBORDER;
    deformObj = GetVar(object, DEFORMOBJ);
    if (deformObj)
    {
	ObjPtr name, defaultIcon;
	/*Drop icon in corral, if need be*/
	name = GetVar(deformObj, NAME);
	defaultIcon = GetVar(deformObj, DEFAULTICON);
	if (defaultIcon)
	{
	    icon = NewObject(defaultIcon, 0);
	    SetVar(icon, NAME, name);
	}
	else
	{
	    icon = NewIcon(0, 0, ICONQUESTION, GetString(name));
        }
	SetVar(icon, REPOBJ, deformObj);
	SetVar(icon, ICONLOC, NULLOBJ);
        DropIconInCorral(corral, icon);
    }

    /*Create the constant deformation control*/
    var = GetVar(object, DEFCONSTANT);
    if (var)
    {
	PrintNumber(tempStr, GetReal(var));
    }
    else
    {
	PrintNumber(tempStr, 0.0);;
    }
    textBox = NewTextBox(MAJORBORDER, left - MINORBORDER, 
			m1 - EDITBOXHEIGHT / 2,
			m1 + EDITBOXHEIGHT / 2, 
			EDITABLE + WITH_PIT + ONE_LINE,
			"Fixed Deformation", tempStr);
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetVar(textBox, REPOBJ, object);
    SetVar(textBox, HELPSTRING, NewString("This text box gives a constant \
deformation when it is selected by the switch."));
    SetVar(textBox, WHICHVAR, NewSymbol(DEFCONSTANT));
    SetTextAlign(textBox, RIGHTALIGN);
    SetMethod(textBox, CHANGEDVALUE, EnterRealNumber);

    /*Create the text box*/
    textBox = NewTextBox(MAJORBORDER - 20, left - MINORBORDER + 20, 
			 m1 - EDITBOXHEIGHT / 2 - TEXTBOXSEP - 2 * TEXTBOXHEIGHT,
			 m1 - EDITBOXHEIGHT / 2 - TEXTBOXSEP,
			 0, "Fixed Deformation Text", "Fixed\nDeformation");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);
    SetVar(textBox, REPOBJ, object);

    /*Create the choice switch*/
    var = GetVar(object, DEFORMSWITCH);
    sw = NewSwitch(left, left + SWITCHWIDTH,
			m2 - cellHeight / 2 - (MAJORBORDER + TEXTBOXHEIGHT + TEXTBOXSEP) / 2,
			m1 + cellHeight / 2 + (MAJORBORDER + TEXTBOXHEIGHT + TEXTBOXSEP) / 2,
			2, 0, var ? GetInt(var) : 0,
			"Deform Switch");
    PrefixList(panelContents, sw);
    SetVar(sw, PARENT, panelContents);
    SetVar(sw, REPOBJ, object);
    SetVar(sw, HELPSTRING, NewString("This switch controls whether the \
deformation of the visualization object comes from a \
field or from a fixed deformation."));
    SetMethod(sw, CHANGEDVALUE, ChangeDSValue);

    left += SWITCHWIDTH + MINORBORDER;

    /*Create the * text box*/
    right = left + MINORBORDER;
    textBox = TemplateTextBox(VisDeformTemplate, "Splat", 0, "*");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextFont(textBox, "Courier-Bold");
    SetTextSize(textBox, 18);
    left = right + MINORBORDER;

    /*Create the deformation factor*/
    var = GetVar(object, DEFFACTOR);
    if (var)
    {
	PrintNumber(tempStr, GetReal(var));
    }
    else
    {
	PrintNumber(tempStr, 0.0);
    }
    right = left + DEFORMEDITWIDTH;
    textBox = TemplateTextBox(VisDeformTemplate, "Deformation Factor", 
			EDITABLE + WITH_PIT + ONE_LINE,
			tempStr);
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetVar(textBox, REPOBJ, object);
    SetVar(textBox, HELPSTRING, NewString("This text box gives a multiplier \
for the deformation provided by the switch."));
    SetVar(textBox, WHICHVAR, NewSymbol(DEFFACTOR));
    SetMethod(textBox, CHANGEDVALUE, EnterRealNumber);
    SetTextAlign(textBox, RIGHTALIGN);

    /*Create the text box*/
    textBox = TemplateTextBox(VisDeformTemplate, "Factor Text",
			0, "Factor");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);
    SetVar(textBox, REPOBJ, object);
    left = right + MINORBORDER;

    /*Create the + text box*/
    right = left + MINORBORDER;
    textBox = TemplateTextBox(VisDeformTemplate, "Plus", 0, "+");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextFont(textBox, "Courier-Bold");
    SetTextSize(textBox, 18);
    left = right + MINORBORDER;

    /*Create the deformation offset*/
    var = GetVar(object, DEFOFFSET);
    if (var)
    {
	PrintNumber(tempStr, GetReal(var));
    }
    else
    {
	PrintNumber(tempStr, 0.0);
    }
    right = left + DEFORMEDITWIDTH;
    textBox = TemplateTextBox(VisDeformTemplate, "Deformation Offset", 
			EDITABLE + WITH_PIT + ONE_LINE, tempStr);
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetVar(textBox, REPOBJ, object);
    SetVar(textBox, HELPSTRING, NewString("This text box gives an offset \
for the deformation provided by the switch, multiplied by the multiplier."));
    SetVar(textBox, WHICHVAR, NewSymbol(DEFOFFSET));
    SetMethod(textBox, CHANGEDVALUE, EnterRealNumber);
    SetTextAlign(textBox, RIGHTALIGN);

    /*Create the text box*/
    textBox = TemplateTextBox(VisDeformTemplate, "Offset Text", 0, "Offset");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);
    SetVar(textBox, REPOBJ, object);
    left = right + MINORBORDER;

    /*Create the Invert Surface check box*/
    left = MAJORBORDER;
    bottom = MAJORBORDER;
    top = bottom + CHECKBOXHEIGHT;
    button = NewCheckBox(left, right, bottom, top, "Invert Surface",
	GetPredicate(object, REVERSESENSE));
    PrefixList(panelContents, button);
    SetVar(button, PARENT, panelContents);
    SetVar(button, REPOBJ, object);
    SetMethod(button, CHANGEDVALUE, ChangeDeformedReverseSense);

    return ObjTrue;
}

static ObjPtr ChangeLightShading(object)
ObjPtr object;
/*Change and object's light shading variable based on a radio group*/
{
    ObjPtr repObj;
    ObjPtr val;

    repObj = GetObjectVar("ChangeLightShading", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(object);

    if (!val)
    {
	return ObjFalse;
    }

    SetVar(repObj, LIGHTSHADING, val);

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeDrawSurface(checkBox)
ObjPtr checkBox;
/*Changes whether an object's surface is drawn*/
{
    ObjPtr repObj;
    repObj = GetObjectVar("ChangeDrawSurface", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, DRAWSURFACE, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeDrawWireframe(checkBox)
ObjPtr checkBox;
/*Changes whether an object's wire frame is drawn*/
{
    ObjPtr repObj;
    repObj = GetObjectVar("ChangeDrawWireframe", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, DRAWWIREFRAME, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeDrawDots(checkBox)
ObjPtr checkBox;
/*Changes whether an object's dots are drawn*/
{
    ObjPtr repObj;
    repObj = GetObjectVar("ChangeDrawDots", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, DRAWDOTS, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeLineDepthCue(checkBox)
ObjPtr checkBox;
/*Changes whether an object's wire frame is drawn with depth cueing*/
{
    ObjPtr repObj;

    if (!hasDepthCue) return ObjFalse;

    repObj = GetObjectVar("ChangeLineDepthCue", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, DEPTHCUELINES, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeDotsDepthCue(checkBox)
ObjPtr checkBox;
/*Changes whether an object's dots are drawn with depth cueing*/
{
    ObjPtr repObj;

    if (!hasDepthCue) return ObjFalse;

    repObj = GetObjectVar("ChangeDotsDepthCue", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, DEPTHCUEDOTS, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeLineAntiAliasing(checkBox)
ObjPtr checkBox;
/*Changes whether an object's wire frame is drawn with anti-aliasing*/
{
    ObjPtr repObj;
    repObj = GetObjectVar("ChangeLineAntiAliasing", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, ANTIALIASLINES, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeDotsAntiAliasing(checkBox)
ObjPtr checkBox;
/*Changes whether an object's dots drawn with anti-aliasing*/
{
    ObjPtr repObj;
    repObj = GetObjectVar("ChangeDotsAntiAliasing", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, ANTIALIASDOTS, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeBiggerDots(checkBox)
ObjPtr checkBox;
/*Changes whether an object's dots are drawn bigger*/
{
    ObjPtr repObj;
    repObj = GetObjectVar("ChangeBiggerDots", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, BIGGERDOTS, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr AddSurfaceControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls appropriate to a surface object to panelContents*/
{
    ObjPtr textBox, name, repObj, control, icon, checkBox, radioGroup, titleBox;
    ObjPtr var;
    ObjPtr panel;
    int width;
    real initValue;
    real shininess, specularity;
    int left, top, right, bottom, mid;
    ObjPtr contents;
    ObjPtr parent;
    real highlightColor[3];
    real dummy, hs[2];

    /*Get the parent*/
    parent = GetObjectVar("AddColoredControls", panelContents, PARENT);

    width = CWINWIDTH - 2 * CORRALBORDER - CWINCORRALWIDTH;

    var = GetVar(object, SHINVAL);
    if (var && IsReal(var))
    {
	shininess = initValue = GetReal(var);
    }
    else
    {
	shininess = initValue = 80.0;
    }

    var = GetVar(object, SPECVAL);
    if (var && IsReal(var))
    {
	specularity = initValue = GetReal(var);
    }
    else
    {
	specularity = initValue = 0.2;
    }

    /*Create the highlights control*/
    top = CWINHEIGHT - MAJORBORDER;
    left = MAJORBORDER;
    bottom = MAJORBORDER;
    control = NewXYControl(left + ICONSIZE / 2 + ICONXYSEP, 
			   left + ICONSIZE / 2 + ICONXYSEP + XYCONTROLWIDTH,
			   top - ICONSIZE / 2 - ICONXYSEP - XYCONTROLWIDTH,
			   top - ICONSIZE / 2 - ICONXYSEP,"Highlights");
    if (!control) return ObjFalse;
    PrefixList(panelContents, control);
    SetVar(control, PARENT, panelContents);
    SetXYControlLimits(control, 5.0, 128.0, 0.0, 1.0);
    SetXYControlValue(control, shininess, specularity);
    SetMethod(control, CHANGEDVALUE, ChangeReflection);
    SetVar(control, HELPSTRING, NewString("This control changes the specular highlights \
of a visualization object.  Move the indicator up to make the highlights brighter and down \
to make them dimmer.  Move it right to make the highlights sharper and left \
to make them dimmer.  The icons at the four corners of the control give a rough \
idea of the effect.  The effect on real visualization objects, however, is often quite \
subtle."));
    SetVar(control, REPOBJ, object);

    /*Create associated icons*/
    icon = NewIcon(left + ICONSIZE / 2,
		   top - XYCONTROLWIDTH - ICONSIZE / 2 - 2 * ICONXYSEP + ICONSHADOW,
		   ICONDIMDIF, (char *) 0);
    if (!icon) return ObjFalse;
    PrefixList(panelContents, icon);
    SetVar(icon, PARENT, panelContents);
    SetMethod(icon, PRESS, (FuncTyp) 0);

    icon = NewIcon(left + ICONSIZE / 2,
		   top - ICONSIZE / 2,
		   ICONBRTDIF, (char *) 0);
    if (!icon) return ObjFalse;
    PrefixList(panelContents, icon);
    SetVar(icon, PARENT, panelContents);
    SetMethod(icon, PRESS, (FuncTyp) 0);

    icon = NewIcon(left + XYCONTROLWIDTH + ICONSIZE / 2 + 2 * ICONXYSEP,
		   top - XYCONTROLWIDTH - ICONSIZE / 2 - 2 * ICONXYSEP + ICONSHADOW,
		   ICONDIMTIGHT, (char *) 0);
    if (!icon) return ObjFalse;
    PrefixList(panelContents, icon);
    SetVar(icon, PARENT, panelContents);
    SetMethod(icon, PRESS, (FuncTyp) 0);

    icon = NewIcon(left + XYCONTROLWIDTH + ICONSIZE / 2 + 2 * ICONXYSEP,
		   top - ICONSIZE / 2,
		   ICONBRTTIGHT, (char *) 0);
    if (!icon) return ObjFalse;
    PrefixList(panelContents, icon);
    SetVar(icon, PARENT, panelContents);
    SetMethod(icon, PRESS, (FuncTyp) 0);

    /*Create the highlights text box*/
    textBox = NewTextBox(left, left + 2 * ICONXYSEP + XYCONTROLWIDTH + ICONSIZE, 
			 top - XYCONTROLWIDTH - ICONSIZE - ICONXYSEP - TEXTBOXHEIGHT,
			 top - XYCONTROLWIDTH - ICONSIZE - ICONXYSEP,
			 0, "Highlights Text", "Highlights");
    if (!textBox) return ObjFalse;
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    mid = left + ICONXYSEP + (XYCONTROLWIDTH + ICONSIZE) / 2;

    /*Create the highlight color control*/
    control = NewColorWheel(mid - COLORWHEELWIDTH / 2, mid + COLORWHEELWIDTH / 2,
			bottom + TEXTBOXHEIGHT + TEXTBOXSEP, 
			bottom + TEXTBOXHEIGHT + TEXTBOXSEP + COLORWHEELWIDTH,
			"Highlight Color");
    PrefixList(panelContents, control);
    SetVar(control, PARENT, panelContents);
    SetVar(control, REPOBJ, object);
    SetVar(control, HELPSTRING, NewString("This color wheel controls the color of the specular highlights \
on a visualization object.  The effects are usually quite subtle and are most useful on objects with \
a fixed base color.")); 

    var = GetVar(object, HIGHLIGHTCOLOR);
    if (var)
    {
	Array2CArray(highlightColor, var);
    }
    else
    {
	highlightColor[0] = 1.0;
	highlightColor[1] = 1.0;
	highlightColor[2] = 1.0;
    }
    RGB2HSV(&(hs[0]), &(hs[1]), &dummy, highlightColor[0], highlightColor[1], highlightColor[2]);
    var = NewRealArray(1, 2L);
    CArray2Array(var, hs);
    SetValue(control, var);
    SetMethod(control, CHANGEDVALUE, ChangeHighlightColor);

    /*Create the text box*/
    textBox = NewTextBox(left, 2 * mid - left, 
			bottom, 
			bottom + TEXTBOXHEIGHT,
			 0, "Highlight Color Text", "Highlight Color");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    left = width - MAJORBORDER - TRANSPARENTWIDTH;

    bottom = MAJORBORDER;

    /*Create the transparent check box, or use the existing one*/
    if (checkBox = GetVar(parent, TRANSPARENTCNT))
    {
	PrefixList(panelContents, checkBox);
    }
    else
    {
	checkBox = NewCheckBox(left, width, 
		bottom,
		bottom + CHECKBOXHEIGHT, "Transparent", GetPredicate(object, ISTRANSPARENT));
	if (!checkBox)
	{
	    return ObjFalse;
	}
	PrefixList(panelContents, checkBox);
	SetVar(checkBox, HELPSTRING,
	    NewString("If this box is checked, the visualization object will be drawn using alpha transparency.  \
It may be useful to turn down the brightness of the object when using this feature.  Transparency \
only works on hardware that supports blending transparency.  If you don't \
have this hardware, try translucency instead."));
	SetVar(checkBox, PARENT, parent);
	SetVar(checkBox, REPOBJ, object);
	SetMethod(checkBox, CHANGEDVALUE, ChangeTransparency);
	SetVar(parent, TRANSPARENTCNT, checkBox);

#ifdef GRAPHICS
	if (!hasTransparency) ActivateButton(checkBox, false);
#endif
    }

    bottom += CHECKBOXHEIGHT + MINORBORDER;

    /*Create the translucent check box, or use the existing one*/
    if (checkBox = GetVar(parent, TRANSLUCENTCNT))
    {
	PrefixList(panelContents, checkBox);
    }
    else
    {
	checkBox = NewCheckBox(left, width, 
		bottom,
		bottom + CHECKBOXHEIGHT, "Translucent", GetPredicate(object, ISTRANSLUCENT));
	if (!checkBox)
	{
	    return ObjFalse;
	}
	PrefixList(panelContents, checkBox);
	SetVar(checkBox, HELPSTRING,
	    NewString("If this box is checked, the visualization object will be drawn using screen door translucency."));
	SetVar(checkBox, PARENT, parent);
	SetVar(checkBox, REPOBJ, object);
	SetMethod(checkBox, CHANGEDVALUE, ChangeTranslucency);
	SetVar(parent, TRANSLUCENTCNT, checkBox);
    }

    bottom += CHECKBOXHEIGHT + MINORBORDER;

    /*Create the two-sided check box*/
    checkBox = NewCheckBox(left, width, 
		bottom,
		bottom + CHECKBOXHEIGHT, "2-Sided Lighting", GetPredicate(object, TWOSIDEDSURFACE));
    if (!checkBox)
    {
	return ObjFalse;
    }
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("If this box is checked, the visualization object will be drawn \
with lighting on both sided of the surface."));
	SetVar(checkBox, PARENT, parent);
	SetVar(checkBox, REPOBJ, object);
	SetMethod(checkBox, CHANGEDVALUE, Change2Sided);

    bottom += CHECKBOXHEIGHT + MINORBORDER;

    /*Create radio button group for light shading*/
    right = width - MAJORBORDER;
    top = bottom + CHECKBOXHEIGHT * 3 + CHECKBOXSPACING + MINORBORDER;

    radioGroup = NewRadioButtonGroup("Light Shading Radio");

    SetVar(radioGroup, HELPSTRING,
	NewString("These radio buttons allow you to change the way a visualization \
object is shaded with the light sources.  Light shading will only work in windows set to \
full color mode.  This control interacts in interesting ways with the color shading.  \
Experiment with different combinations."));
    /*Title box around it*/
    titleBox = NewTitleBox(left - MINORBORDER, right + MINORBORDER,
		bottom,
		top + TITLEBOXTOP + MINORBORDER,
		"Light Shading");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    checkBox = NewRadioButton(left, right, top - CHECKBOXHEIGHT, top,
		"None");
    SetVar(checkBox, HELPSTRING,
	NewString("This button specifies that the visualization object will \
not be shaded with light at all.  This will produce a silhouette effect when the \
color is fixed and will produce an intensely colored surface with no \
lighting cues when the color is from a field."));
    AddRadioButton(radioGroup, checkBox);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    checkBox = NewRadioButton(left, right, top - CHECKBOXHEIGHT, top,
		"Flat");
    SetVar(checkBox, HELPSTRING,
	NewString("This button specifies that the visualization object will \
be flatly shaded with light.  This may be faster on some systems than smooth light \
shading.  The combination of flat light and flat color shading may be even faster."));
    AddRadioButton(radioGroup, checkBox);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    checkBox = NewRadioButton(left, right, top - CHECKBOXHEIGHT, top,
		"Smooth");
    SetVar(checkBox, HELPSTRING,
	NewString("This button specifies that the visualization object will \
be smoothly shaded with light.  This may be slower on some systems than flat light \
shading."));
    AddRadioButton(radioGroup, checkBox);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    /*Add the radio button group*/
    PrefixList(panelContents, radioGroup);
    SetVar(radioGroup, PARENT, panelContents);

    /*Set its value based on color shading*/
    var = GetVar(object, LIGHTSHADING);
    if (var)
    {
	SetValue(radioGroup, var);
    }
    else
    {
	SetValue(radioGroup, NewInt(0));
    }

    /*Set its REPOBJ and CHANGEDVALUE, now that it's all set*/
    SetVar(radioGroup, REPOBJ, object);
    SetMethod(radioGroup, CHANGEDVALUE, ChangeLightShading);

    /*Add checkboxes for draw surface and draw wire frame*/
    top = CWINHEIGHT - MAJORBORDER;
    right = width - MAJORBORDER;
    left = right - SCDRAWBOXLENGTH;

    checkBox = NewCheckBox(left, right, top - CHECKBOXHEIGHT, top,
		"Draw Surface", GetPredicate(object, DRAWSURFACE));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, the surface of the visualization will \
be drawn.  When it is not checked, the surface will not be drawn."));
    SetMethod(checkBox, CHANGEDVALUE, ChangeDrawSurface);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    return ObjTrue;
}

static ObjPtr ChangeVisObjLineWidth(control)
ObjPtr control;
/*Changes the linewidth of a vis object according to control*/
{
    ObjPtr repObj;
    int width;
    ObjPtr value;

    repObj = GetObjectVar("ChangeVisObjLineWidth", control, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(control);
    SetVar(repObj, LINEWIDTH, NewReal((GetReal(value))));

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr AddLineControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls appropriate to a line object to panelContents*/
{
    ObjPtr checkBox, textBox, slider;
    ObjPtr var;

    /*Add checkbox for draw wire frame*/
    checkBox = TemplateCheckBox(VisWireFrameTemplate, 
	"Draw Lines", GetPredicate(object, DRAWWIREFRAME));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, a wire frame representation of the visualization will \
be drawn.  When it is not checked, the wire frame will not be drawn."));
    SetMethod(checkBox, CHANGEDVALUE, ChangeDrawWireframe);

    checkBox = TemplateCheckBox(VisWireFrameTemplate, "Depth Cueing", GetPredicate(object, DEPTHCUELINES));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, the wire frame will be drawn with depth \
cueing, which makes closer parts of the object appear brighter.  You may need to \
adjust the near and far clipping planes in the Observer controls to bring out the full \
range of brightness.  When it is not checked, the wire frame will be drawn without depth cueing."));
    SetMethod(checkBox, CHANGEDVALUE, ChangeLineDepthCue);
    if (!hasDepthCue)
    {
	ActivateButton(checkBox, false);
    }

    checkBox = TemplateCheckBox(VisWireFrameTemplate, "Antialiasing", GetPredicate(object, ANTIALIASLINES));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, the wire frame will be drawn antialiased.  \
This will make the lines appear smoother, while sacrificing some speed.  \
When it is not checked, the wire frame will be drawn without antialiasing.\n\
\n\
Antialiasing only works if your computer supports it and the renderer is in \
full color mode.  Some models of workstation cannot display antialised lines of \
width greater than one.  Antialiasing may interact with other objects in the scene.  \
In general, it is good in scenes where there are few or no surfaces.  Experiment \
to find out if antialiasing is good for your data."));
    SetMethod(checkBox, CHANGEDVALUE, ChangeLineAntiAliasing);
    if (!hasAntialiasedLines)
    {
	ActivateButton(checkBox, false);
    }

    /*Make the line width slider*/
    slider = TemplateSlider(VisWireFrameTemplate, "Width Slider", SCALE);
    PrefixList(panelContents, slider);
    SetVar(slider, PARENT, panelContents);
    SetSliderRange(slider, 10.0, 0.0, 1.0);
    SetSliderScale(slider, 2.0, 1.0, 0.0, "%g");
    var = GetVar(object, LINEWIDTH);
    SetValue(slider, var ? var : NewReal(1.0));
    SetVar(slider, REPOBJ, object);
    SetMethod(slider, CHANGEDVALUE, ChangeVisObjLineWidth);
    SetVar(slider, HELPSTRING, NewString("This slider controls the width of \
the wire frame of the visualization object in screen pixels."));

    /*Make a legend*/
    textBox = TemplateTextBox(VisWireFrameTemplate, "Line Width Legend", 0, "Line Width");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, CENTERALIGN);	
    SetVar(textBox, HELPSTRING, NewString("This text boxc controls the width of \
the wire frame of the visualization object in screen pixels."));

    /*Make a slider readout*/
    textBox = TemplateTextBox(VisWireFrameTemplate, "Width Slider Readout", EDITABLE + WITH_PIT + ONE_LINE, "");
    SetVar(textBox, REPOBJ, object);
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, RIGHTALIGN);
    SliderReadout(slider, textBox);

    /*And "pixels"*/
    textBox = TemplateTextBox(VisWireFrameTemplate, "Pixels Legend", 0, "Pixels");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);

    return ObjTrue;
}

static ObjPtr AddDotsControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls appropriate to a dots object to panelContents*/
{
    ObjPtr checkBox, textBox, slider;
    ObjPtr var;

    /*Add checkbox for draw wire frame*/
    checkBox = TemplateCheckBox(VisDotsTemplate, 
	"Draw Dots", GetPredicate(object, DRAWDOTS));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, a dots representation of the visualization will \
be drawn.  When it is not checked, the dots will not be drawn."));
    SetMethod(checkBox, CHANGEDVALUE, ChangeDrawDots);

    checkBox = TemplateCheckBox(VisDotsTemplate, "Depth Cueing", GetPredicate(object, DEPTHCUEDOTS));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, the dots will be drawn with depth \
cueing, which makes closer parts of the object appear brighter.  You may need to \
adjust the near and far clipping planes in the Observer controls to bring out the full \
range of brightness.  When it is not checked, the dots will be drawn without depth cueing."));
    SetMethod(checkBox, CHANGEDVALUE, ChangeDotsDepthCue);
    if (!hasDepthCue)
    {
	ActivateButton(checkBox, false);
    }

    checkBox = TemplateCheckBox(VisDotsTemplate, "Antialiasing", GetPredicate(object, ANTIALIASDOTS));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, the dots will be drawn antialiased.  \
This will make the dots appear smoother, while sacrificing some speed.  \
When it is not checked, the dots will be drawn without antialiasing.\n\
\n\
Antialiasing only works if your computer supports it and the renderer is in \
full color mode.  Some models of workstation cannot display antialised dots of \
soze greater than one.  Antialiasing may interact with other objects in the scene.  \
In general, it is good in scenes where there are few or no surfaces.  Experiment \
to find out if antialiasing is good for your data."));
    SetMethod(checkBox, CHANGEDVALUE, ChangeDotsAntiAliasing);
    if (!hasAntialiasedPoints)
    {
	ActivateButton(checkBox, false);
    }

    checkBox = TemplateCheckBox(VisDotsTemplate, "Bigger Dots", GetPredicate(object, BIGGERDOTS));
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, the dots will be drawn a little larger.  \
On some systems, bigger dots cannot be anti-aliased"));
    SetMethod(checkBox, CHANGEDVALUE, ChangeBiggerDots);

#if 0
    /*Make the line width slider*/
    slider = TemplateSlider(VisWireFrameTemplate, "Width Slider", SCALE);
    PrefixList(panelContents, slider);
    SetVar(slider, PARENT, panelContents);
    SetSliderRange(slider, 10.0, 0.0, 1.0);
    SetSliderScale(slider, 2.0, 1.0, 0.0, "%g");
    var = GetVar(object, LINEWIDTH);
    SetValue(slider, var ? var : NewReal(1.0));
    SetVar(slider, REPOBJ, object);
    SetMethod(slider, CHANGEDVALUE, ChangeVisObjLineWidth);
    SetVar(slider, HELPSTRING, NewString("This slider controls the width of \
the wire frame of the visualization object in screen pixels."));

    /*Make a legend*/
    textBox = TemplateTextBox(VisWireFrameTemplate, "Line Width Legend", 0, "Line Width");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, CENTERALIGN);	
    SetVar(textBox, HELPSTRING, NewString("This text boxc controls the width of \
the wire frame of the visualization object in screen pixels."));

    /*Make a slider readout*/
    textBox = TemplateTextBox(VisWireFrameTemplate, "Width Slider Readout", EDITABLE + WITH_PIT + ONE_LINE, "");
    SetVar(textBox, REPOBJ, object);
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, RIGHTALIGN);
    SliderReadout(slider, textBox);

    /*And "pixels"*/
    textBox = TemplateTextBox(VisWireFrameTemplate, "Pixels Legend", 0, "Pixels");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
#endif

    return ObjTrue;
}

Bool GetBounds(object, bounds)
ObjPtr object;
real bounds[6];
/*Gets the bounds of an object*/
{
    int k;
    real *meat;
    ObjPtr boundsArray;

    MakeVar(object, BOUNDS);
    boundsArray = GetVar(object, BOUNDS);
    if (boundsArray)
    {
	meat = ELEMENTS(boundsArray);
	for (k = 0; k < 6; ++k)
	{
	    bounds[k] = meat[k];
	}
	return true;
    }
    else
    {
	return false;
    }
}

static Bool GetTicParams(object, lowTic, nTics, ticSpace, initMinor, majorEvery) 
ObjPtr object;
real lowTic[3];
int nTics[3];
real ticSpace[3];
int initMinor[3];
int majorEvery[3];
/*Returns tic parameters for object object into 
  lowTic[d]	=	Value of lowest tic mark for dim d
  nTics[d]	=	Number of total tics for dim d
  ticSpace[d]	=	Spacing between tic marks
  initMinor[d]	=	Number of minor tics to start for dim d
  majorEvery[d] =	A major tic every majorEvery[d] + 1 tics
*/
{
    ObjPtr formObj;
    real bounds[6];
    int k;
    ObjPtr repObj;

    /*Get the bounds*/
    if (!GetBounds(object, bounds))
    {
	return false;
    }

    repObj = GetVar(object, MAINDATASET);
    if (!repObj) repObj = GetObjectVar("GetTicParams", object, REPOBJ);
    if (!repObj)
    {
	return false;
    }

    /*See if the object has a data form*/
    formObj = GetVar(repObj, DATAFORM);
    if (formObj)
    {
	ObjPtr dimsArray;
	real dims[3];
	/*Make major tics go around grid points*/

	dimsArray = GetFixedArrayVar("GetTicParams", formObj, DIMENSIONS, 1, 3L);
	if (!dimsArray)
	{
	    return false;
	}
	Array2CArray(dims, dimsArray);

	for (k = 0; k < 3; ++k)
	{
	    lowTic[k] = bounds[k + k];
	    nTics[k] = (int) (dims[k] + 4.0 * (dims[k] - 1));
	    ticSpace[k] = (bounds[k + k + 1] - bounds[k + k]) / (dims[k] - 1.0) / 5;
	    initMinor[k] = 0;
	    majorEvery[k] = 4;
	}
	return true;
    }
    else
    {
	/*Guess tic marks*/
	return false;
    }
}

static ObjPtr DrawBounded(object)
ObjPtr object;
/*Draw stuff around a bounded object*/
{
#ifdef GRAPHICS
    ObjPtr flagsObj;
    int flags;
    real bounds[6], scaledBounds[6];
    real maxSize = 0;
    ObjPtr dataset;
    ObjPtr var;
    Bool drawEditBox;

    if (DQ_OUTLINE < minDrawingQuality || DQ_OUTLINE > maxDrawingQuality)
    {
	/*Not supposed to draw the outline*/
	return ObjFalse;
    }

    dataset = GetVar(object, MAINDATASET);
    if (!dataset) dataset = GetObjectVar("DrawBounded", object, REPOBJ);
    if (!dataset)
    {
	return ObjFalse;
    }

    flagsObj = GetVar(object, BBFLAGS);
    if (flagsObj)
    {
	flags = GetInt(flagsObj);
    }
    else
    {
	flags = 0;
    }

    if (GetBounds(object, bounds))
    {
	if (bounds[1] - bounds[0] > maxSize) maxSize = bounds[1] - bounds[0];
	if (bounds[3] - bounds[2] > maxSize) maxSize = bounds[3] - bounds[2];
	if (bounds[5] - bounds[4] > maxSize) maxSize = bounds[5] - bounds[4];

	scaledBounds[0] = bounds[0];
	scaledBounds[1] = bounds[1];
	scaledBounds[2] = bounds[2];
	scaledBounds[3] = bounds[3];
	scaledBounds[4] = bounds[4];
	scaledBounds[5] = bounds[5];

	/*Draw inner corner, etc. first with R/O Z-buffer*/
	backface(TRUE);

	NudgeFarther();
	if (flags & BBINNERCORNER)
	{
	    /*Draw the inner corner*/

	    qualityDrawnSoFar = MAX(qualityDrawnSoFar, DQ_OUTLINE);

	    /*Back and front*/
	    SetUIColor(UIGRAY25);
	    pmv(scaledBounds[1], scaledBounds[3], scaledBounds[4]);
	    pdr(scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    pdr(scaledBounds[0], scaledBounds[3], scaledBounds[5]);
	    pdr(scaledBounds[0], scaledBounds[3], scaledBounds[4]);
	    pclos();

	    pmv(scaledBounds[1], scaledBounds[2], scaledBounds[4]);
	    pdr(scaledBounds[0], scaledBounds[2], scaledBounds[4]);
	    pdr(scaledBounds[0], scaledBounds[2], scaledBounds[5]);
	    pdr(scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    pclos();

	    /*Left and right*/
	    SetUIColor(UIGRAY12);
	    pmv(scaledBounds[0], scaledBounds[3], scaledBounds[4]);
	    pdr(scaledBounds[0], scaledBounds[3], scaledBounds[5]);
	    pdr(scaledBounds[0], scaledBounds[2], scaledBounds[5]);
	    pdr(scaledBounds[0], scaledBounds[2], scaledBounds[4]);
	    pclos();
	    
	    pmv(scaledBounds[1], scaledBounds[2], scaledBounds[4]);
	    pdr(scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    pdr(scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    pdr(scaledBounds[1], scaledBounds[3], scaledBounds[4]);
	    pclos();
	}

	if (flags & BBCEILING)
	{
	    /*Draw the ceiling*/
	    SetUIColor(UIGRAY37);
	    pmv(scaledBounds[0], scaledBounds[2], scaledBounds[5]);
	    pdr(scaledBounds[0], scaledBounds[3], scaledBounds[5]);
	    pdr(scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    pdr(scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    pclos();
	}

	if (flags & BBFLOOR)
	{
	    /*Draw the floor*/
	    SetUIColor(UIGRAY37);
	    pmv(scaledBounds[0], scaledBounds[2], scaledBounds[4]);
	    pdr(scaledBounds[1], scaledBounds[2], scaledBounds[4]);
	    pdr(scaledBounds[1], scaledBounds[3], scaledBounds[4]);
	    pdr(scaledBounds[0], scaledBounds[3], scaledBounds[4]);
	    pclos();
	}
	NudgeCloser();
	NudgeCloser();

	backface(FALSE);


	/*See if we need to draw the edit box*/
	drawEditBox = false;
#if 0
	if (IsSelected(object))
	{
	    var = GetVar(curSpace, EDITTOOL);
	    if (var)
	    {
		if (GetInt(var) == ST_3DFINGER)
		{
		    drawEditBox = true;
		}
	    }
	}
#endif

	if (drawEditBox)
	{
	    SetLineWidth(2);
	    SetUIColor(UIWHITE);

	    /*Draw bottom square*/
	    qualityDrawnSoFar = MAX(qualityDrawnSoFar, DQ_OUTLINE);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[2], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[3], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[3], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[2], scaledBounds[4]);

	    /*Draw top square*/
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[5], scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[5], scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[5], scaledBounds[0], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[5], scaledBounds[0], scaledBounds[2], scaledBounds[5]);

	    /*Draw remaining sides*/
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[4], scaledBounds[0], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[4], scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[3], scaledBounds[5]);

	    SetLineWidth(1);
	}
	else if (flags & BBWIREFRAME)
	{
	    /*Draw the wire frame*/

	    if (flags & BBTHICK)
	    {
	        SetLineWidth(2);
	    }
	    SetUIColor(UIGRAY50);

	    /*Draw bottom square*/
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[2], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[3], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[3], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[2], scaledBounds[4]);

	    /*Draw top square*/
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[5], scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[5], scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[5], scaledBounds[0], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[5], scaledBounds[0], scaledBounds[2], scaledBounds[5]);

	    /*Draw remaining sides*/
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[4], scaledBounds[0], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[4], scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[3], scaledBounds[5]);

	    SetLineWidth(1);
	    qualityDrawnSoFar = MAX(qualityDrawnSoFar, DQ_OUTLINE);
	}
	else if (maxDrawingQuality == DQ_OUTLINE &&
		 qualityDrawnSoFar <= DQ_MIN)
	{
	    /*Do the outline anyway*/
	    SetUIColor(UIRED);

	    /*Draw bottom square*/
	    qualityDrawnSoFar = MAX(qualityDrawnSoFar, DQ_OUTLINE);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[2], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[3], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[3], scaledBounds[4]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[2], scaledBounds[4]);

	    /*Draw top square*/
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[5], scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[5], scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[5], scaledBounds[0], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[5], scaledBounds[0], scaledBounds[2], scaledBounds[5]);

	    /*Draw remaining sides*/
	    DrawSpaceLine(scaledBounds[0], scaledBounds[2], scaledBounds[4], scaledBounds[0], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[2], scaledBounds[4], scaledBounds[1], scaledBounds[2], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[1], scaledBounds[3], scaledBounds[4], scaledBounds[1], scaledBounds[3], scaledBounds[5]);
	    DrawSpaceLine(scaledBounds[0], scaledBounds[3], scaledBounds[4], scaledBounds[0], scaledBounds[3], scaledBounds[5]);
	}

	if (flags & (BBXMAJOR |	BBXMINOR | BBYMAJOR | BBYMINOR | BBZMAJOR | BBZMINOR))
	{
	    /*Draw the tic marks*/
	    ObjPtr var;
	    real ticDensity[3], ticLength[3];
	    double maxSize;
	    maxSize = bounds[1] - bounds[0];
	    maxSize = MAX(bounds[3] - bounds[2], maxSize);
	    maxSize = MAX(bounds[5] - bounds[4], maxSize);

	    var = GetFixedArrayVar("DrawBounded", object, TICDENSITY, 1, 3L);
	    if (var)
	    {
		Array2CArray(ticDensity, var);
	    }
	    else
	    {
		ticDensity[0] = ticDensity[1] = ticDensity[2] = 10.0;
	    }

	    MakeVar(object, TICLENGTH);
	    var = GetFixedArrayVar("DrawBounded", object, TICLENGTH, 1, 3L);
	    if (var)
	    {
		Array2CArray(ticLength, var);
	    }
	    else
	    {
		ticLength[0] = ticLength[1] = ticLength[2] = 10.0;
	    }

	    SetupFont("Helvetica", 14);

	    if ((flags & (BBXMAJOR | BBXMINOR)) && (bounds[1] > bounds[0]))
	    {
		double majorWidth;
		int nTics;
		long temp;
		double minorWidth, curValue;
		int k;

		/*Do the x axis*/
		CalcGoodSteps(maxSize, (int)(ticDensity[0] * 10.0), 10,
			&majorWidth, &nTics);

		minorWidth = majorWidth / nTics;

		temp = bounds[0] / majorWidth;
		curValue = temp * majorWidth;

		while (curValue > bounds[0])
		{
		    curValue -= majorWidth;
		}
		k = 0;

		while (curValue < bounds[0])
		{
		    ++k;
		    if (k >= nTics) k = 0;

		    curValue += minorWidth;
		}

		/*Now actually draw them*/
		if (ABS(curValue) < maxSize * 1.0E-6) curValue = 0.0;
		while (curValue <= bounds[1] + maxSize * 1.0E-6)
		{
		    int strOff;
		    if (k == 0 && (flags & BBXMAJOR))
		    {
			/*Major tic*/
			SetUIColor(UIGRAY50);
		    	DrawSpaceLine(curValue, bounds[2], bounds[4],
			   		curValue, bounds[2] - ticLength[0],
				        bounds[4]);
			PrintNumber(tempStr, (real) curValue);
			DrawSpaceString(curValue, bounds[2] - ticLength[0] * LABELFACTOR,
				        bounds[4], tempStr);
		    }
		    else if (flags & BBXMINOR)
		    {
			/*Minor tic*/
			SetUIColor(UIGRAY50);
		    	DrawSpaceLine(curValue, bounds[2], bounds[4],
			   		curValue, bounds[2] - ticLength[0] * MINORTICFACTOR,
				        bounds[4]);
		    }

		    curValue += minorWidth;
		    if (ABS(curValue) < maxSize * 1.0E-6) curValue = 0.0;
		    ++k;
		    if (k >= nTics) k = 0;
		}
	    }
	    if ((flags & (BBYMAJOR | BBYMINOR)) && (bounds[3] > bounds[2]))
	    {
		double majorWidth;
		int nTics;
		long temp;
		double minorWidth, curValue;
		int k;

		/*Do the y axis*/
		CalcGoodSteps(maxSize, (int)(ticDensity[1] * 10.0), 10,
			&majorWidth, &nTics);

		minorWidth = majorWidth / nTics;

		temp = bounds[2] / majorWidth;
		curValue = temp * majorWidth;

		while (curValue > bounds[2])
		{
		    curValue -= majorWidth;
		}
		k = 0;

		while (curValue < bounds[2])
		{
		    ++k;
		    if (k >= nTics) k = 0;

		    curValue += minorWidth;
		}

		/*Now actually draw them*/
		if (ABS(curValue) < maxSize * 1.0E-6) curValue = 0.0;
		while (curValue <= bounds[3] + maxSize * 1.0E-6)
		{
		    int strOff;
		    if (k == 0 && (flags & BBYMAJOR))
		    {
			/*Major tic*/
			SetUIColor(UIGRAY50);
		    	DrawSpaceLine(bounds[0], curValue, bounds[4],
			   		bounds[0] - ticLength[1], curValue,
				        bounds[4]);
			sprintf(tempStr, "%lg", curValue);
			DrawSpaceString(bounds[0] - ticLength[1] * LABELFACTOR, curValue,
				        bounds[4], tempStr);
		    }
		    else if (flags & BBYMINOR)
		    {
			/*Minor tic*/
			SetUIColor(UIGRAY50);
		    	DrawSpaceLine(bounds[0], curValue, bounds[4],
			   		bounds[0] - ticLength[1] * MINORTICFACTOR, curValue,
				        bounds[4]);
		    }

		    curValue += minorWidth;
		    if (ABS(curValue) < maxSize * 1.0E-6) curValue = 0.0;
		    ++k;
		    if (k >= nTics) k = 0;
		}
	    }
	    if ((flags & (BBZMAJOR | BBZMINOR)) && (bounds[5] > bounds[4]))
	    {
		double majorWidth;
		int nTics;
		long temp;
		double minorWidth, curValue;
		int k;

		/*Do the z axis*/
		CalcGoodSteps(maxSize, (int)(ticDensity[2] * 10.0), 10,
			&majorWidth, &nTics);

		minorWidth = majorWidth / nTics;

		temp = bounds[4] / majorWidth;
		curValue = temp * majorWidth;

		while (curValue > bounds[4])
		{
		    curValue -= majorWidth;
		}
		k = 0;

		while (curValue < bounds[4])
		{
		    ++k;
		    if (k >= nTics) k = 0;

		    curValue += minorWidth;
		}

		/*Now actually draw them*/
		if (ABS(curValue) < maxSize * 1.0E-6) curValue = 0.0;
		while (curValue <= bounds[5] + maxSize * 1.0E-6)
		{
		    int strOff;
		    if (k == 0 && (flags & BBZMAJOR))
		    {
			/*Major tic*/
			SetUIColor(UIGRAY50);
		    	DrawSpaceLine(bounds[0], bounds[2], curValue,
			   		bounds[0] - ticLength[2],
					bounds[2] - ticLength[2],
				        curValue);
			PrintNumber(tempStr, (real) curValue);
			DrawSpaceString(bounds[0] - ticLength[2] * LABELFACTOR,
					bounds[2] - ticLength[2] * LABELFACTOR,
				        curValue, tempStr);
		    }
		    else if (flags & BBZMINOR)
		    {
			/*Minor tic*/
			SetUIColor(UIGRAY50);
		    	DrawSpaceLine(bounds[0], bounds[2], curValue,
			   		bounds[0] - ticLength[2] * MINORTICFACTOR,
					bounds[2] - ticLength[2] * MINORTICFACTOR,
				        curValue);
		    }

		    curValue += minorWidth;
		    if (ABS(curValue) < maxSize * 1.0E-6) curValue = 0.0;
		    ++k;
		    if (k >= nTics) k = 0;
		}
	    }
	}
	if (flags & BBAXISNAMES)
	{
	    /*Draw the names of the axes*/
	    SetUIColor(UIGRAY50);
		    
	    SetupFont("Helvetica", 14);

	    /*X axis*/
	    MakeVar(object, XNAME);
	    var = GetStringVar("DrawBounded", object, XNAME);
	    DrawSpaceString((scaledBounds[1] + scaledBounds[0]) / 2.0,
		 scaledBounds[2] - maxSize * AXISFACTOR,
		 scaledBounds[4], var ? GetString(var) : "X");
		
	    /*Y axis*/
	    MakeVar(object, YNAME);
	    var = GetStringVar("DrawBounded", object, YNAME);
	    DrawSpaceString(scaledBounds[0] - maxSize * AXISFACTOR,
		 (scaledBounds[3] + scaledBounds[2]) / 2.0,
		 scaledBounds[4], var ? GetString(var) : "Y");

	    /*Z axis*/
	    MakeVar(object, ZNAME);
	    var = GetStringVar("DrawBounded", object, ZNAME);
	    DrawSpaceString(scaledBounds[0] - maxSize * AXISFACTOR,
		 scaledBounds[2] - maxSize * AXISFACTOR,
		 (scaledBounds[5] + scaledBounds[4]) / 2.0, var ? GetString(var) : "Z");
	}
	NudgeFarther();
    }
    else
    {
	return ObjFalse;
    }
#endif
    return ObjTrue;
}

static ObjPtr MakePicColored(object)
ObjPtr object;
/*Makes the picture object in object colored*/
{
    ObjPtr colorObj;
    ObjPtr surface;

    surface = GetPictureVar("MakePicColored", object, SURFACE);
    if (!surface) return ObjFalse;
    /*Make it colored or not according to COLORS and COLOROBJ*/
    if (GetPredicate(object, COLORS) && (colorObj = GetVar(object, COLOROBJ)))
    {
	/*Have to make it colored by the object*/
	ColorPictureByObject(surface, colorObj, GetPredicate(object, INTERPCOLORS));
    }
    SetVar(object, PICCOLORED, ObjTrue);
    return ObjTrue;
}

static ObjPtr MakePicDeformed(object)
ObjPtr object;
/*Makes the picture object in object deformed*/
{
    ObjPtr surface;

    MakeVar(object, SURFACE);
    surface = GetPictureVar("MakePicDeformed", object, SURFACE);
    if (!surface) return ObjFalse;

    SetupDeformation(object);

    /*Make it deformed or not*/
    DeformPictureByObject(surface);

    SetVar(object, PICDEFORMED, ObjTrue);
    return ObjTrue;
}

static ObjPtr MakeGeoPicColored(object)
ObjPtr object;
/*Makes the picture object in object colored*/
{
    ObjPtr colorObj;
    ObjPtr surface;

    surface = GetPictureVar("MakeGeoPicColored", object, SURFACE);
    if (!surface) return ObjFalse;

    /*Make it colored or not according to COLORS and COLOROBJ*/
    if (GetPredicate(object, COLORS) && (colorObj = GetVar(object, COLOROBJ)))
    {
	/*Have to make it colored by the object*/
	if (colorObj == GetVar(object, REPOBJ))
	{
	    ObjPtr data, eternalData;
	    /*Color it by itself*/
	    MakeVar(colorObj, CURDATA);
	    data = GetVar(colorObj, CURDATA);
	    eternalData = GetVar(colorObj, ETERNALPART);
	    if (eternalData)
	    {
		PicItemPtr curItems, destItems;

		destItems = ((PicPtr) surface) -> items;

		curItems = ((PicPtr) eternalData) -> items;
		destItems = ColorItemsByItems(destItems, object, curItems);

		curItems = ((PicPtr) data) -> items;
		destItems = ColorItemsByItems(destItems, object, curItems);
	    }
	    else
	    {
		ColorPictureByPicture(surface, object, data);
	    }
	}
	else
	{
	    ColorPictureByObject(surface, colorObj, GetPredicate(object, INTERPCOLORS));
	}
    }
    SetVar(object, PICCOLORED, ObjTrue);
    return ObjTrue;
}

static ObjPtr DrawVisSurface(object)
ObjPtr object;
/*Draw a surface vis object, by default, gets picture*/
{
#ifdef GRAPHICS
    ObjPtr repObj;

    if (GetPredicate(object, ISTRANSPARENT) != drawingTransparent)
    {
	/*Don't draw if not the same pass*/
	return ObjFalse;
    }

    /*Get the object's main dataset*/
    repObj = GetVar(object, MAINDATASET);
    if (!repObj) repObj = GetObjectVar("DrawVisSurface", object, REPOBJ);
    if (repObj)
    {
	Bool drawSurface;
	real baseColor[3];		/*Base color of the object*/
	ObjPtr var;
	ObjPtr surface;
	ObjPtr colors;

	drawSurface = GetPredicate(object, DRAWSURFACE);
	if (drawSurface)
	{
	    if (DQ_FULL < minDrawingQuality ||
		DQ_FULL > maxDrawingQuality)
	    {
		drawSurface = false;
	    }
	}
	else if (maxDrawingQuality == DQ_FULL &&
		 qualityDrawnSoFar <= DQ_MIN)
	{
	    /*Surface is at the top, draw it anyway*/
	    drawSurface = true;
	}

	if (!drawSurface)
	{
	    /*Nothing to draw, return*/
	    return ObjFalse;
	}

	if (drawSurface)
	{
	    qualityDrawnSoFar = MAX(qualityDrawnSoFar, DQ_FULL);
	}

	var = GetVar(object, BASECOLOR);
	if (var)
	{
	    Array2CArray(baseColor, var);
	}
	else
	{
	    baseColor[0] = 1.0;
	    baseColor[1] = 1.0;
	    baseColor[2] = 1.0;
	}

	MakeVar(object, CPALETTE);
	colors = GetVar(object, CPALETTE);
	if (!colors)
	{
	    return ObjFalse;
	}

	/*Get the surface*/
	MakeVar(object, SURFACE);
	surface = GetVar(object, SURFACE);
	if (!surface)
	{
	    return ObjFalse;
	}

	/*Set the palette for subsequent draws*/
	SetPalette(colors);

	if (drawSurface)
	{
	    /*OK, so everything exists.  Fine.  Now draw it.*/
	    real specular, shininess, brightness;
	    int lightShading, colorShading;
	    int matIndex;
	    real specColor[3];

	    /*Get the light shading of the surface*/
	    MakeVar(object, LIGHTSHADING);
	    var = GetVar(object, LIGHTSHADING);
	    if (var)
	    {
		lightShading = GetInt(var);
	    }
	    else
	    {
		lightShading = NOLIGHT;
	    }

	    /*Get the color shading of the surface*/
	    if (GetPredicate(object, COLORS) && GetVar(object, COLOROBJ))
	    {
		/*There is a color field.  Shade according to value of COLORSHADING*/
		colorShading = GetPredicate(object, COLORSHADING) ? SMOOTHCOLOR : MONOCOLOR;
	    }
	    else
	    {
		/*There is no color field.  Shade to nocolors.*/
		colorShading = NOCOLOR;
	    }

	    /*Make the material*/
	    var = GetVar(object, SPECVAL);
	    if (var && IsReal(var))
	    {
		specular = GetReal(var);
	    }
	    else
	    {
		specular = 0.2;
	    }
	    var = GetVar(object, HIGHLIGHTCOLOR);
	    if (var && IsRealArray(var) && RANK(var) == 1 && DIMS(var)[0] == 3)
	    {
		Array2CArray(specColor, var);
		specColor[0] *= specular;
		specColor[1] *= specular;
		specColor[2] *= specular;
	    }
	    else
	    {
		specColor[0] = specular;
		specColor[1] = specular;
		specColor[2] = specular;
	    }
	    var = GetVar(object, SHINVAL);
	    if (var && IsReal(var))
	    {
		shininess = GetReal(var);
	    }
	    else
	    {
		shininess = 80.0;
	    }
	    var = GetVar(object, BRIGHTNESS);
	    if (var && IsReal(var))
	    {
		brightness = GetReal(var);
	    }
	    else
	    {
		brightness = 1.0;
	    }

	    matIndex = 0;
	    material[matIndex++] = SPECULAR;
	    material[matIndex++] = specColor[0];
	    material[matIndex++] = specColor[1];
	    material[matIndex++] = specColor[2];
	    material[matIndex++] = SHININESS;
	    material[matIndex++] = shininess;
	    material[matIndex++] = DIFFUSE;
	    material[matIndex++] = baseColor[0] * brightness;
	    material[matIndex++] = baseColor[1] * brightness;
	    material[matIndex++] = baseColor[2] * brightness;
	    material[matIndex++] = LMNULL;

	    lmdef(DEFMATERIAL, MESHMATERIAL, matIndex, material);
	    if (rgbp)
	    {
		lmbind(MATERIAL, MESHMATERIAL);
	    }

	    if (GetPredicate(object, ISTRANSLUCENT))
	    {
		setpattern(GREYPAT);
	    }
	    else
	    {
		setpattern(SOLIDPAT);
	    }

	    /*Set the lighting model*/
	    if (GetPredicate(object, TWOSIDEDSURFACE))
	    {
		TwoSided(true);
	    }

	    if (hasDithering)
	    {
#ifdef DT_ON
		dither(DT_ON);
#endif
	    }
	    drawingQuality = DQ_FULL;
	    DrawPicture(surface, drawingTransparent, lightShading, colorShading, colors);
	    if (hasDithering)
	    {
#ifdef DT_OFF
		dither(DT_OFF);
#endif
	    }
	    if (GetPredicate(object, TWOSIDEDSURFACE))
	    {
		TwoSided(false);
	    }
	    setpattern(SOLIDPAT);
	    lmcolor(LMC_COLOR);
	}
    }
    else
    {
	return ObjFalse;
    }
#endif
    return ObjTrue;
}

static ObjPtr DrawVisLines(object)
ObjPtr object;
/*Draw the lines of a surface vis object, by default, gets picture*/
{
#ifdef GRAPHICS
    ObjPtr repObj;

    if (drawingTransparent)
    {
	/*Don't draw on transparent pass*/
	return ObjFalse;
    }

    /*Get the object's main dataset*/
    repObj = GetVar(object, MAINDATASET);
    if (!repObj) repObj = GetObjectVar("DrawVisLines", object, REPOBJ);
    if (repObj)
    {
	Bool drawWireFrame;
	real baseColor[3];		/*Base color of the object*/
	ObjPtr var;
	ObjPtr surface;
	ObjPtr colors;
	int colorShading;
	Bool antiAlias = false;

	/*Get the color shading of the surface*/
	MakeVar(object, COLORSHADING);
	/*Get the color shading of the surface*/
	if (GetPredicate(object, COLORS) && GetVar(object, COLOROBJ))
	{
	    /*There is a color field.  Shade according to value of COLORSHADING*/
	    colorShading = GetPredicate(object, COLORSHADING) ? SMOOTHCOLOR : MONOCOLOR;
	}
	else
	{
	    /*There is no color field.  Shade to nocolors.*/
	    colorShading = NOCOLOR;
	}

	drawWireFrame = GetPredicate(object, DRAWWIREFRAME);
	if (drawWireFrame)
	{
	    /*If it's outside the range, don't draw a thing*/
	    if (DQ_WIREFRAME < minDrawingQuality ||
		DQ_WIREFRAME > maxDrawingQuality)
	    {
		drawWireFrame = false;
	    }
	}
	else if (maxDrawingQuality == DQ_WIREFRAME &&
		 qualityDrawnSoFar <= DQ_MIN)
	{
	    /*Wire frame is the top, draw it anyway*/
	    drawWireFrame = true;
	}

	if (!drawWireFrame)
	{
	    /*Nothing to draw, return*/
	    return ObjFalse;
	}

	if (drawWireFrame)
	{
	    qualityDrawnSoFar = MAX(qualityDrawnSoFar, DQ_WIREFRAME);
	}

	var = GetVar(object, BASECOLOR);
	if (var)
	{
	    Array2CArray(baseColor, var);
	}
	else
	{
	    baseColor[0] = 1.0;
	    baseColor[1] = 1.0;
	    baseColor[2] = 1.0;
	}

	MakeVar(object, CPALETTE);
	colors = GetVar(object, CPALETTE);
	if (!colors)
	{
	    return ObjFalse;
	}

	/*Get the surface*/
	surface = GetVar(object, SURFACE);
	MakeVar(object, SURFACE);
	if (!surface)
	{
	    return ObjFalse;
	}

	/*Set the palette for subsequent draws*/
	SetPalette(colors);

	var = GetVar(object, LINEWIDTH);
	if (var)
	{
	    int width;
	    width = GetReal(var);
	    if (width < 1) width = 1;
	    SetLineWidth(width);
	}

	if (hasAntialiasedLines && rgbp && GetPredicate(object, ANTIALIASLINES))
	{
	    linesmooth(SML_ON);
	    blendfunction(BF_SA, BF_MSA);
	    subpixel(TRUE);
	    antiAlias = true;
	}

	drawingQuality = DQ_WIREFRAME;
	NudgeCloser();
	NudgeCloser();
	DrawPicture(surface, drawingTransparent, GetPredicate(object, DEPTHCUELINES) ? DEPTHCUELIGHT : NOLIGHT, colorShading, colors);
	NudgeFarther();
	NudgeFarther();

	SetLineWidth(1);
	if (antiAlias)
	{
	    linesmooth(SML_OFF);
	    blendfunction(BF_ONE, BF_ZERO);
	    subpixel(FALSE);
	}
    }
    else
    {
	return ObjFalse;
    }
#endif
    return ObjTrue;
}

static ObjPtr PickVisDots(object)
ObjPtr object;
/*Draws all the dots in an object for picking*/
{
    ObjPtr picture;

    MakeVar(object, SURFACE);
    picture = GetVar(object, SURFACE);
    if (picture)
    {
	PickCanonicalPictureVertices(picture);
    }
    return ObjTrue;
}

static ObjPtr DrawVisDots(object)
ObjPtr object;
/*Draw the dots of a surface vis object, by default, gets picture*/
{
#ifdef GRAPHICS
    ObjPtr repObj;

    if (drawingTransparent)
    {
	/*Don't draw on transparent pass*/
	return ObjFalse;
    }

    /*Get the object's main dataset*/
    repObj = GetVar(object, MAINDATASET);
    if (!repObj) repObj = GetObjectVar("DrawVisLines", object, REPOBJ);
    if (repObj)
    {
	Bool drawDots;
	real baseColor[3];		/*Base color of the object*/
	ObjPtr var;
	ObjPtr surface;
	ObjPtr colors;
	int colorShading;
	Bool antiAlias = false;
	Bool biggerDots = false;

	/*Get the color shading of the surface*/
	MakeVar(object, COLORSHADING);
	/*Get the color shading of the surface*/
	if (GetPredicate(object, COLORS) && GetVar(object, COLOROBJ))
	{
	    /*There is a color field.  Shade according to value of COLORSHADING*/
	    colorShading = GetPredicate(object, COLORSHADING) ? SMOOTHCOLOR : MONOCOLOR;
	}
	else
	{
	    /*There is no color field.  Shade to nocolors.*/
	    colorShading = NOCOLOR;
	}

	drawDots = GetPredicate(object, DRAWDOTS);
	if (drawDots)
	{
	    /*If it's outside the range, don't draw a thing*/
	    if (DQ_DOTS < minDrawingQuality ||
		DQ_DOTS > maxDrawingQuality)
	    {
		drawDots = false;
	    }
	}
	else if (maxDrawingQuality == DQ_DOTS &&
		 qualityDrawnSoFar <= DQ_MIN)
	{
	    /*Dots is the top, draw it anyway*/
	    drawDots = true;
	}

	if (!drawDots)
	{
	    /*Nothing to draw, return*/
	    return ObjFalse;
	}

	qualityDrawnSoFar = MAX(qualityDrawnSoFar, DQ_DOTS);

	var = GetVar(object, BASECOLOR);
	if (var)
	{
	    Array2CArray(baseColor, var);
	}
	else
	{
	    baseColor[0] = 1.0;
	    baseColor[1] = 1.0;
	    baseColor[2] = 1.0;
	}

	MakeVar(object, CPALETTE);
	colors = GetVar(object, CPALETTE);
	if (!colors)
	{
	    return ObjFalse;
	}

	/*Get a surface*/
	MakeVar(object, SURFACE);
	surface = GetVar(object, SURFACE);
	if (!surface)
	{
	    return ObjFalse;
	}

	/*Set the palette for subsequent draws*/
	SetPalette(colors);


	if (hasAntialiasedPoints && rgbp && GetPredicate(object, ANTIALIASDOTS))
	{
	    pntsmooth(SML_ON);
	    blendfunction(BF_SA, BF_MSA);
	    subpixel(TRUE);
	    antiAlias = true;
	}

	if (GetPredicate(object, BIGGERDOTS))
	{
	    SetPointWidth(2);
	    biggerDots = 0;
	}
	else
	{
	    SetPointWidth(1);
	}

	drawingQuality = DQ_DOTS;
	NudgeCloser();
	NudgeCloser();
	DrawPicture(surface, drawingTransparent, GetPredicate(object, DEPTHCUEDOTS) ? DEPTHCUELIGHT : NOLIGHT, colorShading, colors);
	NudgeFarther();
	NudgeFarther();

	if (antiAlias)
	{
	    pntsmooth(SML_OFF);
	    blendfunction(BF_ONE, BF_ZERO);
	    subpixel(FALSE);
	}

	if (biggerDots)
	{
	    SetPointWidth(1);
	}
    }
    else
    {
	return ObjFalse;
    }
#endif
    return ObjTrue;
}

static ObjPtr MakeGeoPictureSurface(object)
ObjPtr object;
/*Makes a geopicture's surface*/
{
    ObjPtr repObj;
    PicPtr picture;
    ObjPtr newPic;
    ObjPtr data;
    ObjPtr eternalData;
    long flags;

    repObj = GetObjectVar("MakeGeoPictureSurface", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    flags = GetDatasetInfo(repObj);
    if (flags & DS_HASGEOMETRY)
    {

    MakeVar(repObj, CURDATA);
    picture = (PicPtr) GetPictureVar("MakeGeoPictureSurface", repObj, CURDATA);
    if (!picture)
    {
	return ObjTrue;
    }

    eternalData = GetVar(repObj, ETERNALPART);

    if (eternalData)
    {
	newPic = ConvertPicture((ObjPtr) eternalData, object);
	ConvertOntoPicture(newPic, (ObjPtr) picture, object);
	SetVar(object, SURFACE, newPic);
    }
    else
    {
	SetVar(object, SURFACE, newPic = ConvertPicture((ObjPtr) picture, object));
    }
    SetVar(newPic, REPOBJ, object);
    }
    else if (flags & DS_HASNEWGEOMETRY)
    {
	/*It's a new geometry*/
	newPic = ConvertDatasetToPicture(repObj, GetVar(object, NORMALSOBJ));
	SetVar(object, SURFACE, newPic);
	SetVar(newPic, REPOBJ, object);
	SetVar(newPic, MAINDATASET, repObj);
    }

    return ObjTrue;
}

static ObjPtr MakeColoredPalette(object)
ObjPtr object;
/*Makes a palette in a color object*/
{
    ObjPtr palette;
    ObjPtr var;
    real brightness;
    ObjPtr colors;

    /*Make a palette if none exists*/
    palette = GetVar(object, CPALETTE);
    if (!palette)
    {
	palette = NewPalette(DEFPALSIZE);
    }
    SetVar(object, CPALETTE, palette);

    /*Get the brightness*/
    var = GetVar(object, BRIGHTNESS);
    if (var && IsReal(var))
    {
	brightness = GetReal(var);
    }
    else
    {
	brightness = 1.0;
    }

    /*Get the colors of the field*/
    if (GetPredicate(object, COLORS) && (colors = GetVar(object, COLOROBJ)))
    {
	/*It gets its colors from a field.*/
	ObjPtr sourcePalette;

	MakeVar(colors, CPALETTE);
	
	sourcePalette = GetVar(colors, CPALETTE);
	if (!sourcePalette)
	{
	    return ObjFalse;
	}

	CopyAttenuatedPalette(palette, sourcePalette, brightness);
    }
    else
    {
	real rgb[3];
	/*Assume it's going to be a plain ramp to BASECOLOR*/

	/*Get the color*/
	var = GetVar(object, BASECOLOR);
	if (var && IsRealArray(var) && RANK(var) == 1 && DIMS(var)[0] == 3)
	{
	    Array2CArray(rgb, var);
	}
	else
	{
	    rgb[0] = rgb[1] = rgb[2] = 1.0;
	}
	SetPaletteMinMax(palette, -1.0, 1.0);
	InterpPalette(palette, 0, 0, 0,
		     (int) (255.0 * brightness * rgb[0]),
		     (int) (255.0 * brightness * rgb[1]),
		     (int) (255.0 * brightness * rgb[2]));
    }
    return ObjTrue;
}

static ObjPtr HideVisObject(object)
ObjPtr object;
/*Hides a vis object*/
{
    SetVar(object, HIDDEN, ObjTrue);
    ImInvalid(object);
    return ObjTrue;
}

static ObjPtr ShowVisObject(object)
ObjPtr object;
/*Shows a vis object*/
{
    SetVar(object, HIDDEN, ObjFalse);
    ImInvalid(object);
    return ObjTrue;
}

static ObjPtr replaceList;

#ifdef PROTO
static void RVHelper(VarsPtr vars, ObjPtr oldVal, ObjPtr newVal)
#else
static void RVHelper(vars, oldVal, newVal)
VarsPtr vars;
ObjPtr oldVal;
ObjPtr newVal;
#endif
/*Helper for ReplaceVars, puts symbols to replace on replaceList*/
{
    if (vars)
    {
	if (vars -> value == oldVal)
	{
	    PrefixList(replaceList, NewSymbol(vars -> name));
	}
	RVHelper(vars -> left, oldVal, newVal);
	RVHelper(vars -> right, oldVal, newVal);
    }
}

#ifdef PROTO
static void ReplaceVars(ObjPtr object, ObjPtr oldVal, ObjPtr newVal)
#else
static void ReplaceVars(object, oldVal, newVal)
ObjPtr object, oldVal, newVal;
#endif
/*For an object, replaces all top level vars with value oldVal to be
  newVal*/
{
    ThingListPtr runner;
    NameTyp id;

    replaceList = NewList();

    RVHelper(object -> vars, oldVal, newVal);

    runner = LISTOF(replaceList);
    while (runner)
    {
	if (IsSymbol(runner -> thing))
	{
	    id = GetSymbolID(runner -> thing);
	    SetVar(object, id, newVal);
	}
	runner = runner -> next;
    }
}

static ObjPtr CloneVisObject(visObj)
ObjPtr visObj;
/*Clones a visualization object*/
{
    FuncTyp method;
    ObjPtr newVis;
    ObjPtr palette;
    ObjPtr mainDataset;

    newVis = Clone(visObj);
    
    MakeVar(visObj, CPALETTE);
    palette = GetVar(visObj, CPALETTE);
    if (palette)
    {
	SetVar(newVis, CPALETTE, ClonePalette(palette));
    }

    mainDataset = GetVar(newVis, MAINDATASET);
    if (mainDataset)
    {
	/*Clone main dataset*/
	method = GetMethod(mainDataset, CLONE);
	if (method)
	{
	    ReplaceVars(newVis, mainDataset, (*method)(mainDataset));
	}
    }
    return newVis;
}

static ObjPtr PrefixMainDataset(list, visObject)
ObjPtr list, visObject;
/*Prefixes the datasets from MAINDATASET or REPOBJ to list*/
{
    ObjPtr mainDataSet;
    mainDataSet = GetVar(visObject, MAINDATASET);
    if (!mainDataSet) mainDataSet = GetVar(visObject, REPOBJ);
    if (mainDataSet)
    {
	PrefixList(list, mainDataSet);
    }
}

void PrefixDatasets(list, visObject)
ObjPtr list, visObject;
/*Prefixes all datastes that visObject uses to list*/
{
    FuncTyp method;
    ObjPtr class;

    class = visObject;
    while (class)
    {
	method = Get1Method(class, PREFIXDATASETS);
	if (method)
	{
	    (*method)(list, visObject);
	}
	class = ClassOf(class);
    }
}

ObjPtr MakeBoundedTicLength(object)
ObjPtr object;
/*Makes an object's tic length*/
{
    ObjPtr var;
    real maxSize;
    real *elements;

    real bounds[6];
    GetBounds(object, bounds);

    maxSize = bounds[1] - bounds[0];
    maxSize = MAX(maxSize, bounds[3] - bounds[2]);
    maxSize = MAX(maxSize, bounds[5] - bounds[4]);

    var = NewRealArray(1, 3L);
    elements = ELEMENTS(var);
    elements[0] = elements[1] = elements[2] = maxSize * MAJORTICSIZE;
    SetVar(object, TICLENGTH, var);

    return ObjTrue;
}

static ObjPtr ChangeSphereSubdivisions(radio)
ObjPtr radio;
/*Changes the sphere subdivisions according to a radio button*/
{
    ObjPtr repObj;
    ObjPtr val;

    repObj = GetObjectVar("ChangeSphereSubdivisions", radio, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(radio);
    SetVar(repObj, SPHERESUBDIV, val);

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeFrustumSubdivisions(radio)
ObjPtr radio;
/*Changes the frustum subdivisions according to a radio button*/
{
    ObjPtr repObj;
    ObjPtr val;

    repObj = GetObjectVar("ChangeFrustumSubdivisions", radio, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(radio);
    SetVar(repObj, FRUSTUMSUBDIV, val);

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr ChangeCapEnds(button)
ObjPtr button;
/*Changes the cap ends according to a button*/
{
    ObjPtr repObj;
    ObjPtr val;

    repObj = GetObjectVar("ChangeCapEnds", button, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    val = GetValue(button);
    SetVar(repObj, CAPENDSP, val);

    ImInvalid(repObj);
    return ObjTrue;
}

static ObjPtr AddGeometryControls(geometry, panelContents)
ObjPtr geometry, panelContents;
/*Adds controls appropriate to a geometry object to panelContents*/
{
    ObjPtr titleBox, button, radio, var, corral, icon, name;
    ObjPtr textBox, defaultIcon;
    int width, left, top, bottom, right;
    int bw;

    width = CWINWIDTH - 2 * CORRALBORDER - CWINCORRALWIDTH;

    left = MAJORBORDER;
    top = CWINHEIGHT - MAJORBORDER;

    /*Make sphere controls*/
    right = PICSPHEREBOXWID + left;
    bottom = top - TITLEBOXTOP - 2 * MINORBORDER - 2 * CHECKBOXSPACING - 3 * CHECKBOXHEIGHT;
    titleBox = NewTitleBox(left, right,
		bottom, top,
		"Spheres");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    /*Make the sphere radio button group*/
    radio = NewRadioButtonGroup("Sphere Facet Group");
    PrefixList(panelContents, radio);
    SetVar(radio, PARENT, panelContents);
    top -= TITLEBOXTOP + MINORBORDER;

    /*Make the buttons*/
    button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
			    top - CHECKBOXHEIGHT, top, "8 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
			    top - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING,
			    top - CHECKBOXHEIGHT - CHECKBOXSPACING, "32 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
			    top - 3 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING,
			    top - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "128 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton((left + right) / 2, right - MINORBORDER,
			    top - CHECKBOXHEIGHT, top, "512 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton((left + right) / 2, right - MINORBORDER,
			    top - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING,
			    top - CHECKBOXHEIGHT - CHECKBOXSPACING, "2048 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton((left + right) / 2, right - MINORBORDER,
			    top - 3 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING,
			    top - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "8192 facets");
    AddRadioButton(radio, button);

    var = GetIntVar("AddGeometryControls", geometry, SPHERESUBDIV);
    if (var)
    {
	SetValue(radio, var);
    }

    SetVar(radio, REPOBJ, geometry);
    SetMethod(radio, CHANGEDVALUE, ChangeSphereSubdivisions);
    SetVar(radio, HELPSTRING, NewString("This controls how many facets are used \
to approximate each sphere in the geometry object.  Higher numbers provide more \
realistic results, especially with specular reflection.  Lower numbers \
result in pictures which can be drawn much more quickly."));

    top = bottom - CHECKBOXSPACING;

    /*Make cylinder controls*/
    right = PICSPHEREBOXWID + left;
    bottom = top - TITLEBOXTOP - 3 * MINORBORDER - 3 * CHECKBOXSPACING - 5 * CHECKBOXHEIGHT;
    titleBox = NewTitleBox(left, right,
		bottom, top,
		"Cylinders and Frusta");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    /*Make the cylinder radio button group*/
    radio = NewRadioButtonGroup("Cylinder Facet Group");
    PrefixList(panelContents, radio);
    SetVar(radio, PARENT, panelContents);
    top -= TITLEBOXTOP + MINORBORDER;

    /*Make the buttons*/
    button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
			    top - CHECKBOXHEIGHT, top, "4 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
			    top - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING,
			    top - CHECKBOXHEIGHT - CHECKBOXSPACING, "8 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
			    top - 3 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING,
			    top - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "16 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton(left + MINORBORDER, (left + right) / 2,
			    top - 4 * CHECKBOXHEIGHT - 3 * CHECKBOXSPACING,
			    top - 3 * CHECKBOXHEIGHT - 3 * CHECKBOXSPACING, "32 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton((left + right) / 2, right - MINORBORDER,
			    top - CHECKBOXHEIGHT, top, "64 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton((left + right) / 2, right - MINORBORDER,
			    top - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING,
			    top - CHECKBOXHEIGHT - CHECKBOXSPACING, "128 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton((left + right) / 2, right - MINORBORDER,
			    top - 3 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING,
			    top - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "256 facets");
    AddRadioButton(radio, button);

    button = NewRadioButton((left + right) / 2, right - MINORBORDER,
			    top - 4 * CHECKBOXHEIGHT - 3 * CHECKBOXSPACING,
			    top - 3 * CHECKBOXHEIGHT - 3 * CHECKBOXSPACING, "512 facets");
    AddRadioButton(radio, button);

    var = GetIntVar("AddGeometryControls", geometry, FRUSTUMSUBDIV);
    if (var)
    {
	SetValue(radio, var);
    }

    SetVar(radio, REPOBJ, geometry);
    SetMethod(radio, CHANGEDVALUE, ChangeFrustumSubdivisions);
    SetVar(radio, HELPSTRING, NewString("This controls how many facets are used \
to approximate each cylinder in the geometry object.  Higher numbers provide more \
realistic results, especially with specular reflection.  Lower numbers \
result in pictures which can be drawn much more quickly."));

    /*Now the end caps button*/
    button = NewCheckBox(left + MINORBORDER, right - MINORBORDER,
			    top - 5 * CHECKBOXHEIGHT - 3 * CHECKBOXSPACING - MINORBORDER,
			    top - 4 * CHECKBOXHEIGHT - 3 * CHECKBOXSPACING - MINORBORDER,
			    "Show end caps", GetPredicate(geometry, CAPENDSP));
    SetVar(button, REPOBJ, geometry);
    SetVar(button, PARENT, panelContents);
    PrefixList(panelContents, button);
    SetMethod(button, CHANGEDVALUE, ChangeCapEnds);
    SetVar(button, HELPSTRING, NewString("This controls whether end caps \
are drawn on the cylinders in this geomtry object.  If it is checked, \
end caps are drawn, and the cylinders appear as solid rods.  If it is not \
checked, the cylinders appear as hollow tubes."));

    return ObjTrue;
}

#ifdef PROTO
void DrawVisObject(ObjPtr object)
#else
void DrawVisObject(object)
ObjPtr object;
#endif
/*Draws a visualization object*/
{
    ObjPtr var;
    float xScale, yScale, zScale;

    /*Get x, y, z, scale*/
    var = GetRealVar("SetupDeformation", object, XSCALE);
    if (var)
    {
	xScale = GetReal(var);
    }
    else 
    {
	xScale = 1.0;
    }
    var = GetRealVar("SetupDeformation", object, YSCALE);
    if (var)
    {
	yScale = GetReal(var);
    }
    else 
    {
	yScale = 1.0;
    }
    var = GetRealVar("SetupDeformation", object, ZSCALE);
    if (var)
    {
	zScale = GetReal(var);
    }
    else 
    {
	zScale = 1.0;
    }

#ifdef GRAPHICS
    pushmatrix();
    scale(xScale, yScale, zScale);
#endif

    DrawObject(object);

#ifdef GRAPHICS
    popmatrix();
#endif
}

static ObjPtr GeometryInit(object)
ObjPtr object;
/*Initializes a geometry object*/
{
    ObjPtr minMax;
    real bounds[6];
    real xySize, zSize;
    ObjPtr colorObj;
    ObjPtr normalsObj;

    colorObj = GetVar(object, REPOBJ);
    if (GetVar(colorObj, CPALETTE))
    {
	SetVar(object, COLOROBJ, colorObj);
	if (colorObj)
	{
	    SetVar(object, COLORS, ObjTrue);
	}
    }
    SetVar(object, MAINDATASET, colorObj);
    if (normalsObj = GetVar(colorObj, NORMALSOBJ))
    {
	SetVar(object, NORMALSOBJ, normalsObj);
    }

    return ObjTrue;
}

static ObjPtr VisualizeVisObject(object)
ObjPtr object;
/*Visualizes a single vis object*/
{
    if (object)
    {
	IdleAllWindows();
	AddObjToSpace(object, FindSpace(selWinInfo), GetVar((ObjPtr) selWinInfo, CORRAL), NULLOBJ, NULLOBJ);
	return ObjTrue;
    }
    return ObjFalse;
}

ObjPtr DummyDeformed(object)
ObjPtr object;
/*Dummy deformed method DIK remove when deformation and scale work on everything.*/
{
    SetVar(object, PICDEFORMED, ObjTrue);
    return ObjTrue;
}

static ObjPtr MakeBoundedXName(object)
ObjPtr object;
/*Makes a bounded vis object's XNAME*/
{
    ObjPtr dataset, name;

    MakeVar(object, MAINDATASET);
    dataset = GetVar(object, MAINDATASET);
    if (!dataset)
    {
	MakeVar(object, REPOBJ);
	dataset = GetVar(object, REPOBJ);
    }
    if (dataset)
    {
	MakeVar(dataset, XNAME);
	name = GetVar(dataset, XNAME);
    }
    else
    {
	name = NULLOBJ;
    }
    if (name)
    {
	SetVar(object, XNAME, name);
    }
    else
    {
	SetVar(object, XNAME, NewString("X"));
    }
}

static ObjPtr MakeBoundedYName(object)
ObjPtr object;
/*Makes a bounded vis object's YNAME*/
{
    ObjPtr dataset, name;

    MakeVar(object, MAINDATASET);
    dataset = GetVar(object, MAINDATASET);
    if (!dataset)
    {
	MakeVar(object, REPOBJ);
	dataset = GetVar(object, REPOBJ);
    }
    if (dataset)
    {
	MakeVar(dataset, YNAME);
	name = GetVar(dataset, YNAME);
    }
    else
    {
	name = NULLOBJ;
    }
    if (name)
    {
	SetVar(object, YNAME, name);
    }
    else
    {
	SetVar(object, YNAME, NewString("Y"));
    }
}

static ObjPtr MakeBoundedZName(object)
ObjPtr object;
/*Makes a bounded vis object's XNAME*/
{
    ObjPtr dataset, name;

    MakeVar(object, MAINDATASET);
    dataset = GetVar(object, MAINDATASET);
    if (!dataset)
    {
	MakeVar(object, REPOBJ);
	dataset = GetVar(object, REPOBJ);
    }
    if (dataset)
    {
	MakeVar(dataset, ZNAME);
	name = GetVar(dataset, ZNAME);
    }
    else
    {
	name = NULLOBJ;
    }
    if (name)
    {
	SetVar(object, ZNAME, name);
    }
    else
    {
	SetVar(object, ZNAME, NewString("Z"));
    }
}

void InitVisObjects()
/*Initializes the visualization objects.*/
{
    ObjPtr icon;
    ObjPtr array;
    ObjPtr list;
    real *elements;

    allVisObjClasses = NewList();
    AddToReferenceList(allVisObjClasses);

    visIcon = NewIcon(0, 0, ICONQUESTION, "Visualization Object");
    AddToReferenceList(visIcon);
    SetVar(visIcon, HELPSTRING,
	NewString("You can create a copy of this visualization, which you can \
then modify, by selecting the icon and choosing the Duplicate item in the Object menu.  \
You can show this visualization object in another window as well by dragging the icon into \
the corral of the other window.  \
You can show controls that affect this visualization by selecting the icon and \
choosing Show Controls from the Object menu."));

    visClass = NewObject(NULLOBJ, 0);
    AddToReferenceList(visClass);
    DeclareDependency(visClass, NAME, MAINDATASET);
    DeclareDependency(visClass, NAME, TEMPLATEP);
    SetMethod(visClass, NAME, MakeVisName);
    SetVar(visClass, CLASSID, NewInt(CLASS_VISOBJ));
    SetVar(visClass, MULTIDRAW, ObjTrue);
    SetVar(visClass, DOUBLECLICK, NewString(OF_SHOW_CONTROLS));
    SetMethod(visClass, NEWCTLWINDOW, ShowVisControls);
    SetMethod(visClass, SHOWCONTROLS, NewControlWindow);
    SetMethod(visClass, HIDE, HideVisObject);
    SetMethod(visClass, SHOW, ShowVisObject);
    list = NewList();
    PrefixList(list, NewSymbol(HIDDEN));
    SetVar(visClass, SNAPVARS, list);
    SetMethod(visClass, CLONE, CloneVisObject);
    SetMethod(visClass, PREFIXDATASETS, PrefixMainDataset);
    SetMethod(visClass, DELETE, DeleteObject);
    SetMethod(visClass, DUPLICATE, CloneObject);
    SetMethod(visClass, LOCALCOPY, MakeLocalCopy);
    SetMethod(visClass, VISUALIZE, VisualizeVisObject);
    SetVar(visClass, XSCALE, NewReal(1.0));
    SetVar(visClass, YSCALE, NewReal(1.0));
    SetVar(visClass, ZSCALE, NewReal(1.0));

    visBounded = NewObject(visClass, 0);
    AddToReferenceList(visBounded);
    SetMethod(visBounded, DRAW, DrawBounded);
    SetMethod(visBounded, ADDCONTROLS, AddBoundedControls);
    SetMethod(visBounded, BOUNDS, MakeFormBounds);
    array = NewRealArray(1, 3L);
    elements = ELEMENTS(array);
    elements[0] = elements[1] = elements[2] = 10.0;
    SetVar(visBounded, TICDENSITY, array);
    SetMethod(visBounded, TICLENGTH, MakeBoundedTicLength);
    icon = NewIcon(0, 0, ICONBOX, "Bounds");
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls for a bounded object, such \
as whether to draw bounds, axes, and tic marks."));
    SetVar(visBounded, CONTROLICON, icon);
    SetMethod(visBounded, XNAME, MakeBoundedXName);
    SetMethod(visBounded, YNAME, MakeBoundedYName);
    SetMethod(visBounded, ZNAME, MakeBoundedZName);

    visWalls = NewObject(visBounded, 0);
    AddToReferenceList(visWalls);
    icon = NewIcon(0, 0, ICONWALLS, "Walls");
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls to display the walls of the \
visualization object."));
    /*SetVar(visWalls, CONTROLICON, icon);*/
    
    visAxes = NewObject(visWalls, 0);
    AddToReferenceList(visAxes);
    icon = NewIcon(0, 0, ICONAXES, "Axes");
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls to display the axes of the \
visualization object."));
    /*SetVar(visAxes, CONTROLICON, icon);*/
    
    /*Class for any colored object*/
    visColored = NewObject(visAxes, 0);
    AddToReferenceList(visColored);
    icon = NewIcon(0, 0, ICONCOLOR, "Color");
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls for a colored object, such \
as whether to color using a fixed color or a scalar field."));
    SetVar(visColored, CONTROLICON, icon);
    SetMethod(visColored, ADDCONTROLS, AddColoredControls);
    SetVar(visColored, COLORSHADING, ObjTrue);	/*Default smooth shaded*/
    DeclareDependency(visColored, PICCOLORED, INTERPCOLORS);
    DeclareDependency(visColored, PICCOLORED, COLORS);
    DeclareDependency(visColored, PICCOLORED, COLOROBJ);
    DeclareDependency(visColored, PICCOLORED, CPALETTE);
    DeclareIndirectDependency(visColored, PICCOLORED, CPALETTE, CHANGED);
    DeclareDependency(visColored, PICCOLORED, TIME);
    SetMethod(visColored, PICCOLORED, MakePicColored);
    SetVar(visColored, INTERPCOLORS, ObjTrue);	/*Interpolate colors*/

    DeclareDependency(visColored, CPALETTE, COLORS);
    DeclareDependency(visColored, CPALETTE, BRIGHTNESS);
    DeclareDependency(visColored, CPALETTE, BASECOLOR);
    DeclareDependency(visColored, CPALETTE, COLOROBJ);
    DeclareIndirectDependency(visColored, CPALETTE, COLOROBJ, PALETTESET);
    SetMethod(visColored, CPALETTE, MakeColoredPalette);
    SetMethod(visColored, PREFIXDATASETS, PrefixColoredDatasets);
    SetVar(visColored, INTERPOLATEP, ObjTrue);

    /*Class for any object with dots*/
    visDots = NewObject(visColored, 0);
    icon = NewIcon(0, 0, ICONDOTS, "Dots");
    SetVar(visDots, DEPTHCUEDOTS, ObjFalse);
    SetMethod(visDots, ADDCONTROLS, AddDotsControls);
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls for drawing dots in the \
visualization object."));
    SetVar(visDots, CONTROLICON, icon);
    SetMethod(visDots, PICKPOINT, PickVisDots);
    SetMethod(visDots, DRAW, DrawVisDots);
    DeclareDependency(visDots, PICCOLORED, SURFACE);
 
    /*Class for any object with lines*/
    visLines = NewObject(visDots, 0);
    SetVar(visLines, DRAWWIREFRAME, ObjTrue);
    SetVar(visLines, DEPTHCUELINES, ObjFalse);
    icon = NewIcon(0, 0, ICONLINES, "Lines");
    SetMethod(visLines, ADDCONTROLS, AddLineControls);
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls for drawing lines in the \
visualization object."));
    SetVar(visLines, CONTROLICON, icon);
    SetMethod(visLines, DRAW, DrawVisLines);

    /*Class for any object with a lit surface*/
    visSurface = NewObject(visLines, 0);
    AddToReferenceList(visSurface);
    SetVar(visSurface, DRAWSURFACE, ObjTrue);
    SetVar(visSurface, DRAWWIREFRAME, ObjFalse);
    icon = NewIcon(0, 0, ICONMATERIAL, "Surface");
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls for a surface, such \
as surface highlights and translucency."));
    SetMethod(visSurface, ADDCONTROLS, AddSurfaceControls);
    SetVar(visSurface, CONTROLICON, icon);
    SetVar(visSurface, LIGHTSHADING, NewInt(2)); /*Default smooth shaded*/
    SetVar(visSurface, SHINVAL, NewReal(80.0));
    SetVar(visSurface, SPECVAL, NewReal(0.2));
    DeclareDependency(visSurface, SURFACE, TIME);
    SetMethod(visSurface, DRAW, DrawVisSurface);

    /*Class for any object with a deformable surface*/
    visDeformed = NewObject(visSurface, 0);
    AddToReferenceList(visDeformed);
    SetMethod(visDeformed, ADDCONTROLS, AddDeformedControls);
    icon = NewIcon(0, 0, ICONDEFORM, "Deform");
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls that deform a visualization object."));
    SetVar(visDeformed, CONTROLICON, icon);

    SetVar(visDeformed, DEFFACTOR, NewReal(1.0));
    DeclareIndirectDependency(visDeformed, SURFACE, DEFORMOBJ, CURDATA);
    DeclareDependency(visDeformed, SURFACE, DEFORMOBJ);
    DeclareDependency(visDeformed, SURFACE, DEFCONSTANT);
    DeclareDependency(visDeformed, SURFACE, DEFOFFSET);
    DeclareDependency(visDeformed, SURFACE, DEFFACTOR);
    DeclareDependency(visDeformed, SURFACE, DEFORMSWITCH);
    DeclareDependency(visDeformed, PICDEFORMED, SURFACE);
    DeclareDependency(visDeformed, PICDEFORMED, TIME);
    /*DIKEO dependency on INTERPwhatever*/
    SetMethod(visDeformed, PICDEFORMED, MakePicDeformed);

    /*Class for a geometry object*/
    visGeometryClass = NewObject(visSurface, 0);
    AddToReferenceList(visGeometryClass);
    SetMethod(visGeometryClass, ADDCONTROLS, AddGeometryControls);
    icon = NewIcon(0, 0, ICONGEOMETRY, "Geometry");
    SetVar(visGeometryClass, CONTROLICON, icon);
    SetVar(visGeometryClass, SPHERESUBDIV, NewInt(2));
    SetVar(visGeometryClass, FRUSTUMSUBDIV, NewInt(2));
    SetVar(visGeometryClass, CAPENDSP, ObjTrue);
    SetMethod(visGeometryClass, INITIALIZE, GeometryInit);

    DeclareDependency(visGeometryClass, SURFACE, SPHERESUBDIV);
    DeclareDependency(visGeometryClass, SURFACE, FRUSTUMSUBDIV);
    DeclareDependency(visGeometryClass, SURFACE, CAPENDSP);
    SetMethod(visGeometryClass, PICCOLORED, MakeGeoPicColored);

    /*Class for a geometry picture*/
    geoPictureClass = NewObject(visGeometryClass, 0);
    AddToReferenceList(geoPictureClass);
    SetVar(geoPictureClass, NAME, NewString("Geometry"));
    icon = NewObject(visIcon, 0);
    SetVar(icon, NAME, NewString("Geometry"));
    SetVar(icon, WHICHICON, NewInt(ICONGEOMETRY));
    SetVar(icon, HELPSTRING,
	NewString("This icon represents a geometry object, which shows a \
geometry dataset directly as a picture in the space."));
    SetVar(geoPictureClass, DEFAULTICON, icon);
    SetMethod(geoPictureClass, BOUNDS, MakeGeoPictureBounds);
    SetMethod(geoPictureClass, SURFACE, MakeGeoPictureSurface);
    DeclareIndirectDependency(geoPictureClass, SURFACE, REPOBJ, CURDATA);
    DeclareDependency(geoPictureClass, SURFACE, TIME);

    InitArrows();
    InitIsosurfaces();
    InitMeshes();
    InitContours();
    InitTraces();
    InitSticks();
    InitPoints();
    InitBalls();
    InitNumbers();

    DefineVisMapping(DS_HASGEOMETRY, -1, -1, -1, geoPictureClass);
    DefineVisMapping(DS_HASNEWGEOMETRY | DS_HASFORM | DS_UNSTRUCTURED, -1, -1, -1, geoPictureClass);
}

void KillVisObjects()
/*Kills the visobjects*/
{
    KillNumbers();
    KillBalls();
    KillPoints();
    KillSticks();
    KillArrows();
    KillContours();
    KillIsosurfaces();
    KillTraces();
    DeleteThing(geoPictureClass);
    DeleteThing(visDeformed);
    DeleteThing(visSurface);
    DeleteThing(visColored);
    DeleteThing(visAxes);
    DeleteThing(visWalls);
    DeleteThing(visBounded);
    DeleteThing(visClass);
    DeleteThing(visIcon);
    while (nVisSerials)
    {
	--nVisSerials;
	free(visSerials[nVisSerials] . name);
    }
    DeleteThing(allVisObjClasses);
}

