CCL Home Page
Up Directory CCL ScianVisObjects
/*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"
#include "ScianScales.h"
#include "ScianFontSystem.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*/
ObjPtr visSized;			/*Class for sizable object*/

Bool drawSolid = false;			/*True iff we want to draw solid*/
real globalFactor, globalOffset, globalFixed;
ObjPtr globalDeformObject;
extern ObjPtr perspecControlClass;	/*Perspective control*/
extern real spaceTime;
ObjPtr allVisObjClasses;

/*Outline box values*/
#define OB_NONE			0
#define OB_LINES		1
#define OB_FRAME		2
#define OB_GIRDERS		3
#define OB_CYLINDERS		4

/*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 HAVE_PROTOTYPES
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 =
		Alloc(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 HAVE_PROTOTYPES
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;
    ObjPtr thisVis;

    /*Always apply some filters*/
    dataset = MainFilters(dataset);

    flags = GetDatasetInfo(dataset);
    flags &= ~DS_TIMEDEPENDENT;
    spatialDim = GetSpatialDim(dataset);
    topDim = GetTopDim(dataset);
    if (flags & DS_VECTOR)
    {
	MakeVar(dataset, NCOMPONENTS);
	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();
	}

	thisVis = NewVis(dataset, visMapping[k] . visClass);
	if (thisVis)
	{
	    PostfixList(list, thisVis);
	    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(MainFilters(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)
    {
	visClass = 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)
    {
	ObjPtr result;
	result = (*method)(retVal);
	if (!IsTrue(result)) return NULLOBJ;
    }

    return retVal;
}

ObjPtr DropInMainDatasetCorral(corral, object, x, y)
ObjPtr corral, object;
int x, y;
/*Drops an icon in a main dataset corral*/
{
    WarnUser(CW_CANNOTDROPINMAIN);
    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;

    MakeVar(visObject, DEFAULTICON);
    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];
    ObjPtr var;

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

    SetCurField(FIELD5, repObj);

    geometry = curFields[FIELD5] . objectInfo;
    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);
    }
    else if (geometry)
    {
	var = GetVar(geometry, CLASSID);
	if (var && GetInt(var) == CLASS_TIMEDOBJ)
	{
	    long k;
	    ObjPtr *elements;
	    real newBounds[6];

	    var = GetVar(geometry, TIMEDATA);

	    bounds[0] = bounds[2] = bounds[4] = plusInf;
	    bounds[1] = bounds[3] = bounds[5] = minusInf;

	    if (var && IsObjArray(var))
	    {
		elements = ELEMENTS(var);
		for (k = 0; k < DIMS(var)[0]; ++k)
		{
		    GetPictureBounds(elements[k], 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]);
		}
	    }
	    else
	    {
		ReportError("MakeGeoPictureBounds", "Time error");
	    }
	    geometry = GetVar(repObj, ETERNALPART);
	    if (geometry)
	    {
		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);
    }

    return ObjTrue;
}

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

    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));
    ImInvalid(repObj);
    SetVar(object, APPEARANCE, ObjTrue);
    return ObjTrue;
}

static ObjPtr MakeReflectionAppearance(object)
ObjPtr object;
/*Makes the appearance of a reflection quality control*/
{
    real shininess, specularity;
    ObjPtr val;
    ObjPtr repObj;
    ObjPtr var;
    real *elements;
    FuncTyp method;

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

    var = GetRealVar("MakeReflectionAppearance", repObj, SHINVAL);
    if (!var)
    {
	return ObjFalse;
    }
    shininess = GetReal(var);

    var = GetRealVar("MakeReflectionAppearance", repObj, SPECVAL);
    if (!var)
    {
	return ObjFalse;
    }
    specularity = GetReal(var);

    val = NewRealArray(1, 2L);
    elements = ELEMENTS(val);
    elements[0] = shininess;
    elements[1] = specularity;

    method = GetMethod(object, CHANGEDVALUE);
    SetMethod(object, CHANGEDVALUE, (FuncTyp) 0);
    SetValue(object, val);
    SetMethod(object, CHANGEDVALUE, method);

    SetVar(object, APPEARANCE, ObjTrue);
    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 + WINUI);
    
    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);

	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));
		SetVar(button, REPOBJ, object);
		SetMethod(button, CHANGEDVALUE, ChangeControlPanelButton);

		SetVar(button, PANEL, panel);

		/*Make a new panel contents just for this button*/
		panelContents = NewList();
		SetVar(panelContents, TYPESTRING, NewString("control panel"));
		SetVar(panelContents, NAME, NewString(name));
		if (var = GetVar(icon, PANELHELP))
		{
		    ObjPtr iconButton;
		    SetVar(panelContents, HELPSTRING, var);
		}
		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)
		{
		    SetVar(button, CONTROLSADDED, ObjFalse);
		    SetMethod(button, ADDCONTROLS, method);
		}
		else
		{
		    SetVar(button, CONTROLSADDED, ObjTrue);
		}
		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();
			    SetVar(panelContents, TYPESTRING, NewString("control panel"));
			    SetVar(panelContents, NAME, NewString(name));
			    if (var = GetVar(icon, PANELHELP))
			    {
				ObjPtr iconButton;
				SetVar(panelContents, HELPSTRING, var);
			    }
			    PrefixList(panelContents, controlField);
			    SetVar(panelContents, PARENT, panel);
			    SetVar(button, PANELCONTENTS, panelContents);
			    SetVar(button, PARENT, panelContents);
			    SetVar(button, REPOBJ, mainDataset);

			    /*Give the button a chance to add controls*/
			    method = GetMethod(mainDataset, ADDCONTROLS);
			    if (method)
			    {
				SetVar(button, CONTROLSADDED, ObjFalse);
				SetMethod(button, ADDCONTROLS, method);
			    }
			    else
			    {
				SetVar(button, CONTROLSADDED, ObjTrue);
			    }
			    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;
}

static char *wallNames[6] =
    {
	"-X Wall",
	"+X Wall",
	"-Y Wall",
	"+Y Wall",
	"-Z Wall (Floor)",
	"+Z Wall (Ceiling)"
    };

#ifdef HAVE_PROTOTYPES
void AppendSpaces(char *s, int nSpaces)
#else
void AppendSpaces(s, nSpaces)
char *s;
int nSpaces;
#endif
/*Appends nSpaces spaces after s*/
{
    while (*s) ++s;

    while (nSpaces--)
    {
	*s++ = ' ';
    }
    *s = '\0';
}

static ObjPtr AddAxesControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls for a visualization object with axes*/
{
    ObjPtr titleBox, checkBox, textBox, var;
    ObjPtr colorWheel, slider;
    int curFlag;
    int left, right, bottom, top, mid, width, i, j, whichWall, k, f, h;
    int l, r, b, t;
    int titleWidth, buttonWidth, axisGroupWidth, rightWidth;
    char buttonName[40];

    width = CWINWIDTH - CORRALBORDER - CWINCORRALWIDTH;
    top = CWINHEIGHT - MINORBORDER;
    rightWidth = (width - 2 * MINORBORDER - SBFUNCTIONNAMEWIDTH);

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

    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    bottom = top - 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("AddAxesControls", 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);
	SetTextAlign(textBox, RIGHTALIGN);
	switch (k)
	{
	    case 0:
		AssocDirectControlWithVar(textBox, object, XNAME);
		break;
	    case 1:
		AssocDirectControlWithVar(textBox, object, YNAME);
		break;
	    case 2:
		AssocDirectControlWithVar(textBox, object, ZNAME);
		break;
	}
	SetVar(textBox, HELPSTRING, NewString("This text box contains the name of the \
indicated axis."));
    }
    top = bottom - SBFUNCTIONSPACING;

    /*Axis name check boxes*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    bottom = top - CHECKBOXHEIGHT;

    textBox = NewTextBox(left, right, 
			top - TEXTBOXHEIGHT + EDITBOXDOWN,
			top + EDITBOXDOWN,
			0, "Show Names Text", "Show Name:"); 
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);

    /*Draw name check boxes*/
    bottom = top - CHECKBOXHEIGHT;

    curFlag = 1;
    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 ? "Centered" : k == 1 ? "Centered " : "Centered  ",
			       0);
	if (!checkBox)
	{
	    return ObjFalse;
	}
        PrefixList(panelContents, checkBox);
	SetVar(checkBox, PARENT, panelContents);
	sprintf(tempStr, "If this box is checked, the name of this axis will be shown at the center of the axis.");
	SetVar(checkBox, HELPSTRING, NewString(tempStr));
	AssocFlagControlWithVar(checkBox, object, DRAWNAMES, curFlag);
	curFlag *= 2;
    }
    top = bottom - CHECKBOXSPACING;

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

    for (k = 0; k < 3; ++k)
    {
	/*Make the major step edit box*/
	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 Major Step" : (k ? "Y Major Step" : "X Major Step"),
		""); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetTextAlign(textBox, RIGHTALIGN);
	AssocIndexedTextRealControlWithVar(textBox, object, AXISMAJORSTEP, k, 0, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
	SetVar(textBox, HELPSTRING, NewString("This text box contains a number \
which gives the step size for major tic marks along the indicated dimension.  \
The major steps are calculated relative to the origin."));
    }
    top = bottom - SBFUNCTIONSPACING;

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

    for (k = 0; k < 3; ++k)
    {
	/*Make the major step edit box*/
	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 Divisions" : (k ? "Y Divisions" : "X Divisions"),
		""); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetTextAlign(textBox, RIGHTALIGN);
	AssocIndexedTextRealControlWithVar(textBox, object, AXISDIVISIONS, k, 1, plusInf, TR_INT_ONLY | TR_NE_TOP);
	SetVar(textBox, HELPSTRING, NewString("This text box contains a number \
which gives the number of minor divisions within each major step.  For example, if the major step \
is 1.0, and the number of divisions is 10, there will be a minor tic every 0.1 units."));
    }
    top = bottom - SBFUNCTIONSPACING;

    /*Tic length controls*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    bottom = top - 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*/
	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"),
		""); 
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panelContents);
	SetTextAlign(textBox, RIGHTALIGN);
	AssocIndexedTextRealControlWithVar(textBox, object, TICLENGTH, k, 0.0, plusInf, 0);
	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."));
    }
    top = bottom - SBFUNCTIONSPACING;

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

    textBox = NewTextBox(left, right, 
			top - TEXTBOXHEIGHT + EDITBOXDOWN,
			top + EDITBOXDOWN,
			0, "Tic Marks Text", "Show Tics:"); 
    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  ",
			       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));
	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));
	AssocFlagControlWithVar(checkBox, object, BBFLAGS, curFlag);
	curFlag *= 4;
    }
    top = bottom - SBFUNCTIONSPACING;

    /*Minor tic mark check boxes*/
    bottom = top - 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  ",
			       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));
	AssocFlagControlWithVar(checkBox, object, BBFLAGS, curFlag);
	curFlag *= 4;
    }
    top = bottom - CHECKBOXSPACING;

    return ObjTrue;
}

static ObjPtr AddWallsControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls for a visualization object with walls*/
{
    ObjPtr titleBox, checkBox, textBox;
    ObjPtr colorWheel, slider;
    int left, right, bottom, top, width, i, j, whichWall, k, f, h;
    int l, r, b, t;
    int titleWidth, buttonWidth;
    char buttonName[40];

    width = CWINWIDTH - CORRALBORDER - CWINCORRALWIDTH;
    left = MINORBORDER;
    right = width - MINORBORDER;
    top = CWINHEIGHT - MINORBORDER;
    bottom = MINORBORDER;

    buttonWidth = (width - 6 * MINORBORDER) / 4;
    titleWidth = buttonWidth;
    titleWidth += 21;
    buttonWidth -= 7;

    for (i = 0; i < 3; ++i)
    {
	for (j = 0; j < 2; ++j)
	{
	    k = j + i * 2;
	    
	    f = 1 << k;

	    l = left;
	    r = l + titleWidth;
	    t = top - k * (CHECKBOXHEIGHT + CHECKBOXSPACING);
	    b = t - CHECKBOXHEIGHT;

	    sprintf(buttonName, "%s:", wallNames[k]);
	    textBox = NewTextBox(l, r, b - 4, t - 4, PLAIN, buttonName, buttonName);
	    PrefixList(panelContents, textBox);
	    SetVar(panelContents, PARENT, textBox);

	    l = r + MINORBORDER;
	    r = l + buttonWidth;

	    strcpy(buttonName, "Wall");
	    AppendSpaces(buttonName, k);
	    checkBox = NewCheckBox(l, r, b, t, buttonName, false);
	    PrefixList(panelContents, checkBox);
	    SetVar(checkBox, PARENT, panelContents);
	    AssocFlagControlWithVar(checkBox, object, WALLPANELFLAGS, f);
	    sprintf(tempStr, "When this check box is on, the %s wall is visible.  \
The Wall Panel, Wall Lines, and Draw control groups at the bottom of the \
window control how visible walls are drawn.", wallNames[k]);
	    SetVar(checkBox, HELPSTRING, NewString(tempStr));
	   
	    l = r + MINORBORDER;
	    r = l + buttonWidth;

	    strcpy(buttonName, "Shadow");
	    AppendSpaces(buttonName, k);
	    checkBox = NewCheckBox(l, r, b, t, buttonName, false);
	    PrefixList(panelContents, checkBox);
	    SetVar(checkBox, PARENT, panelContents);
	    AssocFlagControlWithVar(checkBox, object, WALLSHADOWFLAGS, f);
	    sprintf(tempStr, "When this check box is on, a black shadown of the \
visualization is shown on the %s wall.", wallNames[k]);
	    SetVar(checkBox, HELPSTRING, NewString(tempStr));
	    
	    l = r + MINORBORDER;
	    r = l + buttonWidth;

	    strcpy(buttonName, "Mirror");
	    AppendSpaces(buttonName, k);
	    checkBox = NewCheckBox(l, r, b, t, buttonName, false);
	    PrefixList(panelContents, checkBox);
	    SetVar(checkBox, PARENT, panelContents);
	    AssocFlagControlWithVar(checkBox, object, WALLMIRRORFLAGS, f);
	    sprintf(tempStr, "When this check box is on, the %s wall acts as a mirror \
for the visualization object.  It's not quite like a real mirror, as the presence of the \
reflection does not depend in any way whether the angle of view included the mirror.  Also, \
facing mirrors do not produce a barbershop effect.  This feature is useful when only one-half \
or one-quarter of a problem domain has been computed and you want to display the rest \
automatically.", wallNames[k]);
	    SetVar(checkBox, HELPSTRING, NewString(tempStr));
	}
    }

    t = top - 6 * (CHECKBOXHEIGHT + CHECKBOXSPACING) - MINORBORDER;

    /*Make the wall panel control group*/
    titleBox = NewTitleBox(left, left + 3 * MINORBORDER + COLORWHEELWIDTH + SLIDERWIDTH,
			   t - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT - TITLEBOXTOP - 2 * MINORBORDER,
			   t, "Wall Panels");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    l = left + MINORBORDER;
    r = l + COLORWHEELWIDTH;
    
    t -= TITLEBOXTOP + MINORBORDER;
    b = t - COLORWHEELWIDTH;

    /*Make the color wheel and its text box*/
    colorWheel = NewColorWheel(l, r, b, t, "Panel Color Wheel");
    SetVar(colorWheel, PARENT, panelContents);
    PrefixList(panelContents, colorWheel);
    AssocColorControlWithVar(colorWheel, object, WALLPANELCOLOR); 
    SetVar(colorWheel, HELPSTRING, NewString("This color wheel controls the \
hue and saturation of the color used to draw the panels of visible walls.  \
The final color is a combination of this hue and saturation and the value, or brightness, \
given by the Value slider."));

    textBox = NewTextBox(l, r, b - TEXTBOXSEP - TEXTBOXHEIGHT, b - TEXTBOXSEP,
		PLAIN, "Panel Color Text", "Color");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, CENTERALIGN);

    l = r + MINORBORDER;

    r = l + SLIDERWIDTH;

    /*Make the brightness slider*/
    slider = NewSlider(l, r, b, t,
		       PLAIN, "Panel Color Value");
    SetVar(slider, PARENT, panelContents);
    PrefixList(panelContents, slider);
    SetSliderRange(slider, 1.0, 0.0, 0.0);
    AssocBrightnessControlWithVar(slider, object, WALLPANELCOLOR);
	SetVar(slider, HELPSTRING, NewString("This slider controls the \
value, or brightness, of the color used to draw panels of visible walls.  \
The final color is a combination of this value and the hue and saturation \
given by the Color color wheel."));

    textBox = NewTextBox(l - MINORBORDER, r + MINORBORDER, b - TEXTBOXSEP - TEXTBOXHEIGHT, b - TEXTBOXSEP,
		PLAIN, "{ame; Value Text", "Value");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, CENTERALIGN);

    l = r + MINORBORDER;

    l += MINORBORDER;
    r = right;
    t = top - 6 * (CHECKBOXHEIGHT + CHECKBOXSPACING) - MINORBORDER;

    /*Make the wall lines control group*/
    titleBox = NewTitleBox(l, l + 3 * MINORBORDER + COLORWHEELWIDTH + SLIDERWIDTH,
			   t - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT - TITLEBOXTOP - 2 * MINORBORDER,
			   t, "Wall Lines");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    l += MINORBORDER;
    r -= MINORBORDER;
    
    t -= TITLEBOXTOP + MINORBORDER;
    b = t - COLORWHEELWIDTH;
    r = l + COLORWHEELWIDTH;

    /*Make the color wheel and its text box*/
    colorWheel = NewColorWheel(l, r, b, t, "Panel Lines Color Wheel");
    SetVar(colorWheel, PARENT, panelContents);
    PrefixList(panelContents, colorWheel);
    AssocColorControlWithVar(colorWheel, object, WALLLINESCOLOR); 
    SetVar(colorWheel, HELPSTRING, NewString("This color wheel controls the \
hue and saturation of the color used to draw lines on visible walls.  \
The final color is a combination of this hue and saturation and the value, or brightness, \
given by the Value slider."));

    textBox = NewTextBox(l, r, b - TEXTBOXSEP - TEXTBOXHEIGHT, b - TEXTBOXSEP,
		PLAIN, "Wall Lines Color Text", "Color");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, CENTERALIGN);

    l = r + MINORBORDER;

    r = l + SLIDERWIDTH;

    /*Make the brightness slider*/
    slider = NewSlider(l, r, b, t,
		       PLAIN, "Wall Lines Color Value");
    SetVar(slider, PARENT, panelContents);
    PrefixList(panelContents, slider);
    SetSliderRange(slider, 1.0, 0.0, 0.0);
    AssocBrightnessControlWithVar(slider, object, WALLLINESCOLOR);
	SetVar(slider, HELPSTRING, NewString("This slider controls the \
value, or brightness, of the color used to draw lines on visible walls.  \
The final color is a combination of this value and the hue and saturation \
given by the Color color wheel."));

    textBox = NewTextBox(l - MINORBORDER, r + MINORBORDER, b - TEXTBOXSEP - TEXTBOXHEIGHT, b - TEXTBOXSEP,
		PLAIN, "Wall Lines Value Text", "Value");
    SetVar(textBox, PARENT, panelContents);
    PrefixList(panelContents, textBox);
    SetTextAlign(textBox, CENTERALIGN);

    /*Put in the draw checkBoxs at the right*/

    l = r + 2 * MINORBORDER;
    r = right;
    t = t + MINORBORDER + TITLEBOXTOP;
    b = b - MINORBORDER - TEXTBOXHEIGHT - TEXTBOXSEP;

    titleBox = NewTitleBox(l, r, b, t, "Draw");
    SetVar(titleBox, PARENT, panelContents);
    PrefixList(panelContents, titleBox);

    l += MINORBORDER;
    t -= TITLEBOXTOP + MINORBORDER;
    r -= MINORBORDER;

    b = t - CHECKBOXHEIGHT;
    checkBox = NewCheckBox(l, r, b, t, "Outline", 
		GetPredicate(object, DRAWOUTLINE));
    SetVar(checkBox, PARENT, panelContents);
    PrefixList(panelContents, checkBox);
    AssocDirectControlWithVar(checkBox, object, DRAWOUTLINE);
    SetVar(checkBox, HELPSTRING, 
	NewString("This check box controls the drawing of the outline of visible walls.  \
When it is on, an outline is drawn around every one of the visible walls.  \
When it is off, the outline is not drawn.  The color is \
given by the Wall Lines controls, and other parameters of the lines such as width and \
antialiasing are set in the Lines control panel.  Unlike the wall panel, the outline \
is visible even when viewed from the outside."));
    t = b - CHECKBOXSPACING;

    b = t - CHECKBOXHEIGHT;
    checkBox = NewCheckBox(l, r, b, t, "Grid", 
		GetPredicate(object, DRAWGRID));
    SetVar(checkBox, PARENT, panelContents);
    PrefixList(panelContents, checkBox);
    AssocDirectControlWithVar(checkBox, object, DRAWGRID);
    SetVar(checkBox, HELPSTRING, 
	NewString("This check box controls the drawing of a grid on visible walls.  \
When it is on, a grid is drawn on every one of the visible walls.  \
The spacing of the grid is given on the major tic marks as defined in \
the Axes control panel.  When the check box is off, the grid is not drawn.  The color is \
given by the Wall Lines controls, and other parameters of the lines such as width and \
antialiasing are set in the Lines control panel.  Unlike the wall panel, the grid \
is visible even when viewed from the outside."));
    t = b - CHECKBOXSPACING;

    b = t - CHECKBOXHEIGHT;
    checkBox = NewCheckBox(l, r, b, t, "Panel ", 
		GetPredicate(object, DRAWBACKGROUND));
    SetVar(checkBox, PARENT, panelContents);
    PrefixList(panelContents, checkBox);
    AssocDirectControlWithVar(checkBox, object, DRAWBACKGROUND);
    SetVar(checkBox, HELPSTRING, 
	NewString("This check box controls the drawing of panels on visible walls.  \
When it is on, a solid, shaded panel is drawn for every one of the visible walls.  \
When it is off, the background is not drawn.  Panels, \
like shadows, are only visible when viewed from the inside.  The color of \
the panel is given by the Wall Panels color wheel and value slider."));
    t = b - CHECKBOXSPACING;

    b = t - CHECKBOXHEIGHT;

    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, button, radio;
    ObjPtr flagsObj, var;
    int flags;
    int curFlag;
    int i, j, k;
    int tbh;
    real bounds[6];

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

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

    GetBounds(object, bounds);

    /*Make control group, from top to bottom*/

    top = CWINHEIGHT - MINORBORDER;
    rightWidth = (width - MINORBORDER - SBFUNCTIONNAMEWIDTH);

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

    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    bottom = top -  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);
	SetTextAlign(textBox, RIGHTALIGN);
	switch (k)
	{
	    case 0:
		AssocDirectControlWithVar(textBox, object, XNAME);
		break;
	    case 1:
		AssocDirectControlWithVar(textBox, object, YNAME);
		break;
	    case 2:
		AssocDirectControlWithVar(textBox, object, ZNAME);
		break;
	}
	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."));
    }
    top = bottom - SBFUNCTIONSPACING;

    /*Min edit boxes*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    bottom =top - 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, PARENT, panelContents);
	SetTextAlign(textBox, RIGHTALIGN);
	AssocIndexedTextRealControlWithVar(textBox, object, BOUNDS, k * 2, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
	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."));
    }
    top = bottom - SBFUNCTIONSPACING;

    /*Max edit boxes*/
    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    bottom = top - 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);
	SetTextAlign(textBox, RIGHTALIGN);
	AssocIndexedTextRealControlWithVar(textBox, object, BOUNDS, k * 2 + 1, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
	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."));
    }
    top = bottom - SBFUNCTIONSPACING;

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

    left = MINORBORDER;
    right = left + SBFUNCTIONNAMEWIDTH;
    bottom = top - 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);
	SetTextAlign(textBox, RIGHTALIGN);
	switch (k)
	{
	    case 0:
		AssocTextRealControlWithVar(textBox, object, XSCALE, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
		break;
	    case 1:
		AssocTextRealControlWithVar(textBox, object, YSCALE, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
		break;
	    case 2:
		AssocTextRealControlWithVar(textBox, object, ZSCALE, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
		break;
	}
	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."));
    }
    top = bottom - SBFUNCTIONSPACING;

    /*Make bounding box controls at bottom*/
    left = MINORBORDER;
    bottom = MINORBORDER;
    top = bottom + COLORWHEELWIDTH + 2 * MINORBORDER + TITLEBOXTOP + TEXTBOXHEIGHT + TEXTBOXSEP;
    right = left + 400;

    titleBox = TemplateTitleBox(BoundsTemplate, "Outline Box");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    /*Make the outline box radio button*/
    radio = NewRadioButtonGroup("Draw Outline");
    SetVar(radio, HELPSTRING, NewString("This radio button group controls whether an \
outline box is drawn around the bounds and how it is drawn."));
    PrefixList(panelContents, radio);
    SetVar(radio, PARENT, panelContents);

    top -= TITLEBOXTOP + MINORBORDER;
    left += MINORBORDER;
    button = TemplateRadioButton(BoundsTemplate,
	"None");
    AddRadioButton(radio, button);
    SetVar(button, HELPSTRING, NewString("If this button is on, no outline box will be \
drawn around the bounds."));
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    button = TemplateRadioButton(BoundsTemplate,
	"Lines");
    AddRadioButton(radio, button);
    SetVar(button, HELPSTRING, NewString("If this button is on, an outline will \
be drawn as solid lines.  The color of the lines is given by the Color and Value \
controls.  Other attributes of the line, such as the line width, are given \
in the Lines control panel."));
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    button = TemplateRadioButton(BoundsTemplate,
	"Frame");
    AddRadioButton(radio, button);
    SetVar(button, HELPSTRING, NewString("If this button is on, an outline will \
be drawn as a solid frame.  The frame looks as if it is made out of rectangular panels \
with rectangular holes.  \
 The color of the frame is given by the Color and Value \
controls.  The thickness of the frame is given by the Thickness text box."));
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    button = TemplateRadioButton(BoundsTemplate,
	"Girders");
    AddRadioButton(radio, button);
    SetVar(button, HELPSTRING, NewString("If this button is on, an outline will \
be drawn as solid girders.  The color of the girders is given by the Color and Value \
controls.  The thickness of the girders is given by the Thickness text box."));
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    button = TemplateRadioButton(BoundsTemplate,
	"Cylinders");
    AddRadioButton(radio, button);
    SetVar(button, HELPSTRING, NewString("If this button is on, an outline will \
be drawn as solid cylinders.  The color of the girders is given by the Color and Value \
controls.  The thickness of the cylinders is given by the Thickness text box."));
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

    AssocDirectControlWithVar(radio, object, BOUNDSBOX);

    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;

    if (!IsDataset(object))
    {
	object = GetVar(object, MAINDATASET);
    }
    if (!IsDataset(object))
    {
	WarnUser(CW_CANNOTDROPNONDATASET);
	return ObjFalse;
    }

    /*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)))
    {
	WarnUser(CW_NOGEOMETRYERROR);
	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;

    if (!IsDataset(object))
    {
	object = GetVar(object, MAINDATASET);
    }
    if (!IsDataset(object))
    {
	WarnUser(CW_CANNOTDROPNONDATASET);
	return ObjFalse;
    }

    /*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))
    {
	WarnUser(CW_NOGEOMETRYERROR);
	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 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, win;
    ObjPtr var;
    int width, left, right, top, cellHeight, m1, m2;
    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*/
    win = parent;
    while (win && !IsWindow(win))
    {
	win = GetVar(win, PARENT);
    }
    button = NewFunctionButton((WinInfoPtr) win, left, left + ONECORRALWIDTH,
			m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP - TEXTBOXHEIGHT - MINORBORDER - BUTTONHEIGHT,
			m2 - ONECORRALHEIGHT / 2 - TEXTBOXSEP - TEXTBOXHEIGHT - MINORBORDER,
			OF_EDIT_PALETTE);
    PrefixList(panelContents, button);
    SetVar(button, PARENT, panelContents);
    SetVar(corral, BUTTON, button);
    SetVar(button, REPOBJ, corral);
    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."));

    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 = GetFixedArrayVar("AddColoredControls", object, BASECOLOR, 1, 3L);
    if (!var)
    {
	real *baseColor;

	var = NewRealArray(1, 3L);
	baseColor = ELEMENTS(var);
	
	baseColor[0] = 1.0;
	baseColor[1] = 1.0;
	baseColor[2] = 1.0;

	SetVar(object, BASECOLOR, var);
    }
    AssocColorControlWithVar(control, object, BASECOLOR);

    /*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*/
    if (!GetVarSurely("AddColoredControls", object, COLORS))
    {
	SetVar(object, COLORS, NewInt(0));
    }
    AssocDirectControlWithVar(sw, object, COLORS);
 
    left += SWITCHWIDTH + MINORBORDER;

    /*Create the brightness slider*/
    var = GetRealVar("AddColoredControls", object, BRIGHTNESS);
    if (!var)
    {
	SetVar(object, BRIGHTNESS, NewReal(1.0));
    }

    slider = NewSlider(left, left + SLIDERWIDTH, 
		       m1 - cellHeight / 2, m1 + cellHeight / 2,
		       PLAIN, "Brightness");
    if (!slider)
    {
	return ObjFalse;
    }
    PrefixList(panelContents, slider);
    SetVar(slider, PARENT, panelContents);
    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."));
    SetSliderRange(slider, 1.0, 0.0, 0.0);
    AssocDirectControlWithVar(slider, object, BRIGHTNESS);

    /*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);
	if (!GetVar(object, ISTRANSLUCENT))
	{
	    SetVar(object, ISTRANSLUCENT, ObjFalse);
	}
	AssocDirectControlWithVar(checkBox, object, ISTRANSLUCENT);
	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);
	if (!GetVar(object, ISTRANSPARENT))
	{
	    SetVar(object, ISTRANSPARENT, ObjFalse);
	}
	AssocDirectControlWithVar(checkBox, object, ISTRANSPARENT);
	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));

    /*Associate it with the variable*/
    AssocDirectControlWithVar(radioGroup, object, COLORSHADING);

    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 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 = GetRealVar("AddDeformedControls", object, DEFCONSTANT);
    if (!var)
    {
	SetVar(object, DEFCONSTANT, NewReal(0.0));
    }
    textBox = NewTextBox(MAJORBORDER, left - MINORBORDER, 
			m1 - EDITBOXHEIGHT / 2,
			m1 + EDITBOXHEIGHT / 2, 
			EDITABLE + WITH_PIT + ONE_LINE,
			"Fixed Deformation", "");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    AssocTextRealControlWithVar(textBox, object, DEFCONSTANT, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
    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);

    /*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 = GetIntVar("AddDeformedControls", 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."));

    if (!var)
    {
	SetVar(object, DEFORMSWITCH, NewInt(0));
    }
    AssocDirectControlWithVar(sw, object, DEFORMSWITCH);

    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 = GetRealVar("AddDeformedControls", object, DEFFACTOR);
    if (!var)
    {
	SetVar(object, DEFFACTOR, NewReal(0.0));
    }
    right = left + DEFORMEDITWIDTH;
    textBox = TemplateTextBox(VisDeformTemplate, "Deformation Factor", 
			EDITABLE + WITH_PIT + ONE_LINE,
			"");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    AssocTextRealControlWithVar(textBox, object, DEFFACTOR, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
    SetVar(textBox, HELPSTRING, NewString("This text box gives a multiplier \
for the deformation provided by the switch."));
    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 = GetRealVar("AddDeformedControls", object, DEFOFFSET);
    if (!var)
    {
	SetVar(object, DEFOFFSET, NewReal(0.0));
    }
    right = left + DEFORMEDITWIDTH;
    textBox = TemplateTextBox(VisDeformTemplate, "Deformation Offset", 
			EDITABLE + WITH_PIT + ONE_LINE, tempStr);
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    AssocTextRealControlWithVar(textBox, object, DEFOFFSET, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
    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));
    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);
    AssocDirectControlWithVar(button, object, REVERSESENSE);
 
    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;
    ObjPtr scale;

    /*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 - MINORBORDER;
    left = MINORBORDER;
    bottom = MINORBORDER;
    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);
    DeclareIndirectDependency(control, APPEARANCE, REPOBJ, SHINVAL);
    DeclareIndirectDependency(control, APPEARANCE, REPOBJ, SPECVAL);
    SetMethod(control, APPEARANCE, MakeReflectionAppearance);
    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, 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)
    {
	real *highlightColor;

	var = NewRealArray(1, 3L);
	highlightColor[0] = 1.0;
	highlightColor[1] = 1.0;
	highlightColor[2] = 1.0;
	SetVar(object, HIGHLIGHTCOLOR, var);
    }
    AssocColorControlWithVar(control, object, HIGHLIGHTCOLOR);

    /*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);
	if (!GetVar(object, ISTRANSPARENT))
	{
	    SetVar(object, ISTRANSPARENT, ObjFalse);
	}
	AssocDirectControlWithVar(checkBox, object, ISTRANSPARENT);
	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);
	if (!GetVar(object, ISTRANSLUCENT))
	{
	    SetVar(object, ISTRANSLUCENT, ObjFalse);
	}
	AssocDirectControlWithVar(checkBox, object, ISTRANSLUCENT);
	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);
    if (!GetVar(object, TWOSIDEDSURFACE)) SetVar(object, TWOSIDEDSURFACE, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, TWOSIDEDSURFACE);

    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)
    {
	SetVar(object, LIGHTSHADING, NewInt(0));
    }

    AssocDirectControlWithVar(radioGroup, object, LIGHTSHADING);

    /*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);
    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."));
    if (!GetVar(object, DRAWSURFACE)) SetVar(object, DRAWSURFACE, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, DRAWSURFACE);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;

#ifdef GRAPHOBJ
    checkBox = NewCheckBox(left, right, top - CHECKBOXHEIGHT, top,
		"Cache Graphics", GetPredicate(object, CACHEGRAPHICS));
    SetVar(checkBox, PARENT, panelContents);
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, the graphics of the visualization \
object will be cached in a graphical object.  This will speed up drawing during interaction \
but will use a lot of memory."));
    if (!GetVar(object, CACHEGRAPHICS)) SetVar(object, CACHEGRAPHICS, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, CACHEGRAPHICS);
    top -= CHECKBOXHEIGHT + CHECKBOXSPACING;
#endif

    return ObjTrue;
}

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

    /*Add checkbox for draw wire frame*/
    checkBox = TemplateCheckBox(VisWireFrameTemplate, 
	"Draw Lines", GetPredicate(object, DRAWWIREFRAME));
    SetVar(checkBox, PARENT, panelContents);
    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."));
    if (!GetVar(object, DRAWWIREFRAME)) SetVar(object, DRAWWIREFRAME, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, DRAWWIREFRAME);

    checkBox = TemplateCheckBox(VisWireFrameTemplate, "Depth Cueing", GetPredicate(object, DEPTHCUELINES));
    SetVar(checkBox, PARENT, panelContents);
    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."));
    if (!GetVar(object, DEPTHCUELINES)) SetVar(object, DEPTHCUELINES, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, DEPTHCUELINES);
    if (!hasDepthCue)
    {
	ActivateButton(checkBox, false);
    }

    checkBox = TemplateCheckBox(VisWireFrameTemplate, "Antialiasing", GetPredicate(object, ANTIALIASLINES));
    SetVar(checkBox, PARENT, panelContents);
    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."));
    if (!GetVar(object, ANTIALIASLINES)) SetVar(object, ANTIALIASLINES, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, ANTIALIASLINES);
    if (!hasAntialiasedLines)
    {
	ActivateButton(checkBox, false);
    }

    scale = TemplateScale(VisWireFrameTemplate, "Width Scale", SO_TOP, false);
    SetScaleStepPixels(scale, 10);
    PrefixList(panelContents, scale);
    SetVar(scale, PARENT, panelContents);

    /*Make the line width slider*/
    slider = TemplateSlider(VisWireFrameTemplate, "Width Slider", SCALE);
    PrefixList(panelContents, slider);
    SetVar(slider, PARENT, panelContents);
    LinkScale(scale, slider);
    var = GetVar(object, LINEWIDTH);
    SetSliderRange(slider, 10.0, 1.0, 1.0);
    AssocDirectControlWithVar(slider, object, LINEWIDTH);
    if (!var) SetVar(object, LINEWIDTH, NewReal(1.0));
    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, RIGHTALIGN);	
    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);
    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."));
    if (!GetVar(object, DRAWDOTS)) SetVar(object, DRAWDOTS, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, DRAWDOTS);

    checkBox = TemplateCheckBox(VisDotsTemplate, "Depth Cueing", GetPredicate(object, DEPTHCUEDOTS));
    SetVar(checkBox, PARENT, panelContents);
    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."));
    if (!GetVar(object, DEPTHCUEDOTS)) SetVar(object, DEPTHCUEDOTS, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, DEPTHCUEDOTS);
    if (!hasDepthCue)
    {
	ActivateButton(checkBox, false);
    }

    checkBox = TemplateCheckBox(VisDotsTemplate, "Antialiasing", GetPredicate(object, ANTIALIASDOTS));
    SetVar(checkBox, PARENT, panelContents);
    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."));
    if (!GetVar(object, ANTIALIASDOTS)) SetVar(object, ANTIALIASDOTS, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, ANTIALIASDOTS);
    if (!hasAntialiasedPoints)
    {
	ActivateButton(checkBox, false);
    }

    checkBox = TemplateCheckBox(VisDotsTemplate, "Bigger Dots", GetPredicate(object, BIGGERDOTS));
    SetVar(checkBox, PARENT, panelContents);
    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 antialiased"));
    if (!GetVar(object, BIGGERDOTS)) SetVar(object, BIGGERDOTS, NewInt(0));
    AssocDirectControlWithVar(checkBox, object, BIGGERDOTS);

    return ObjTrue;
}

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

    MakeVar(object, BOUNDS);
    boundsArray = GetVar(object, BOUNDS);
    if (boundsArray)
    {
	meat = ELEMENTS(boundsArray);
	for (k = 0; k < 6; ++k)
	{
	    bounds[k] = meat[k];
	}
	if (bounds[0] > bounds[1])
	{
	    temp = bounds[0];
	    bounds[0] = bounds[1];
	    bounds[1] = temp;
	}
	if (bounds[2] > bounds[3])
	{
	    temp = bounds[2];
	    bounds[2] = bounds[3];
	    bounds[3] = temp;
	}
	if (bounds[4] > bounds[5])
	{
	    temp = bounds[4];
	    bounds[4] = bounds[5];
	    bounds[5] = temp;
	}
	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;
    int outlineBox;

    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];
    }
    else
    {
	return NULLOBJ;
    }

    var = GetVar(object, BOUNDSBOX);
    if (var)
    {
	outlineBox = GetInt(var);
    }
    else
    {
	outlineBox = 0;
    }

    if (DQ_OUTLINE >= minDrawingQuality && DQ_OUTLINE <= maxDrawingQuality)
    {
	/*Draw the outline*/

	/*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 (outlineBox == OB_LINES)
	{
	    /*Lines*/
	    Bool antiAlias = false;

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

	    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]);

	    if (hasAntialiasedLines && rgbp && GetPredicate(object, ANTIALIASLINES))
	    {
		linesmooth(SML_ON);
		blendfunction(BF_SA, BF_MSA);
		subpixel(TRUE);
		antiAlias = true;
	    }
	    SetLineWidth(1);
	    if (antiAlias)
	    {
		linesmooth(SML_OFF);
		blendfunction(BF_ONE, BF_ZERO);
		subpixel(FALSE);
	    }
	}

	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 (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 majorStep[3], nDivisions[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 = GetVar(object, LINEWIDTH);
	    if (var)
	    {
		int width;
		width = GetReal(var);
		if (width < 1) width = 1;
		SetLineWidth(width);
	    }

	    MakeVar(object, AXISMAJORSTEP);
	    var = GetFixedArrayVar("DrawBounded", object, AXISMAJORSTEP, 1, 3L);
	    if (var)
	    {
		Array2CArray(majorStep, var);
	    }
	    else
	    {
		majorStep[0] = majorStep[1] = majorStep[2] = 1.0;
	    }

	    MakeVar(object, AXISDIVISIONS);
	    var = GetFixedArrayVar("DrawBounded", object, AXISDIVISIONS, 1, 3L);
	    if (var)
	    {
		Array2CArray(nDivisions, var);
	    }
	    else
	    {
		nDivisions[0] = nDivisions[1] = nDivisions[2] = 1.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;
	    }

	    SetUIFont(UIBOLDLARGEFONT);

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

		/*Do the x axis*/
		majorWidth = majorStep[0];
		minorWidth = majorWidth / nDivisions[0];
		nTics = nDivisions[0];

		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*/
		majorWidth = majorStep[1];
		minorWidth = majorWidth / nDivisions[1];
		nTics = nDivisions[1];

		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*/
		majorWidth = majorStep[2];
		minorWidth = majorWidth / nDivisions[2];
		nTics = nDivisions[2];

		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;
		}
	    }

	    SetLineWidth(1);
	}
	var = GetIntVar("DrawBounded", object, DRAWNAMES);
	if (var)
	{
	    flags = GetInt(var);
	}
	else
	{
	    flags = 0;
	}
	if (flags)
	{
	    /*Draw the names of the axes*/
	    SetUIColor(UIGRAY50);
		    
	    SetUIFont(UIBOLDLARGEFONT);

	    if (flags & 1)
	    {
		/*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");
	    }
		
	    if (flags & 2)
	    {
		/*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");
	    }

	    if (flags & 4)
	    {
		/*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");
	    }
	}
    }
    if (DQ_FULL >= minDrawingQuality && DQ_FULL <= maxDrawingQuality &&
	outlineBox > OB_LINES)
    {
	/*Draw the solid frame*/
	real frameWidth;

	/*KLUDGE change later*/
	frameWidth = (bounds[1] - bounds[0]) * 0.05;

	if (rgbp)
	{
	    real *elements;
	    int matIndex;
#if 0
	    var = GetVar(object, WALLPANELCOLOR);
	    if (var)
	    {
		elements = ELEMENTS(var);
	    }
	    else
#endif
	    {
		elements = 0;
	    }

	    matIndex = 0;
	    material[matIndex++] = DIFFUSE;
	    material[matIndex++] = elements ? elements[0] : 0.5;
	    material[matIndex++] = elements ? elements[1] : 0.5;
	    material[matIndex++] = elements ? elements[2] : 0.5;
	    material[matIndex++] = LMNULL;
	    lmdef(DEFMATERIAL, VISMATERIAL, matIndex, material);
	    lmbind(MATERIAL, VISMATERIAL);
	    lmcolor(LMC_DIFFUSE);
	}

	OptimizeColor();

	if (outlineBox == OB_FRAME)
	{
	    backface(TRUE);

	    if (!rgbp) SetUIColor(UIGRAY62);

	    /*-x*/
	    FrameWideSpaceRect(
		bounds[0], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[2], bounds[4],
		rgbp ? 1.0 : 0.0, 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		bounds[0], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[4],
		rgbp ? -1.0 : 0.0, 0.0, 0.0, frameWidth);

	    /*+x*/
	    FrameWideSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[4],
		rgbp ? -1.0 : 0.0, 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		bounds[1], bounds[3], bounds[4],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[2], bounds[5],
		bounds[1], bounds[2], bounds[4],
		rgbp ? 1.0 : 0.0, 0.0, 0.0, frameWidth);

	    if (!rgbp) SetUIColor(UIGRAY25);

	    /*-y*/
	    FrameWideSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, rgbp ? 1.0 : 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		bounds[1], bounds[2], bounds[5],
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[4],
		0.0, rgbp ? -1.0 : 0.0, 0.0, frameWidth);

	    /*+y*/
	    FrameWideSpaceRect(
		bounds[1], bounds[3], bounds[4],
		bounds[1], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[4],
		0.0, rgbp ? -1.0 : 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		bounds[0], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[4],
		0.0, rgbp ? 1.0 : 0.0, 0.0, frameWidth);

	    if (!rgbp) SetUIColor(UIGRAY12);

	    /*floor*/
	    FrameWideSpaceRect(
		bounds[0], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[4],
		0.0, 0.0, rgbp ? 1.0 : 0.0, frameWidth);
	    FrameWideSpaceRect(
		bounds[0], bounds[2], bounds[4],
		bounds[0], bounds[3], bounds[4],
		bounds[1], bounds[3], bounds[4],
		bounds[1], bounds[2], bounds[4],
		0.0, 0.0, rgbp ? -1.0 : 0.0, frameWidth);

	    /*ceiling*/
	    FrameWideSpaceRect(
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, 0.0, rgbp ? -1.0 : 0.0, frameWidth);
	    FrameWideSpaceRect(
		bounds[1], bounds[2], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[2], bounds[5],
		0.0, 0.0, rgbp ? 1.0 : 0.0, frameWidth);

	    backface(FALSE);
	}
	else if (outlineBox == OB_GIRDERS)
	{
	    real expBounds[6];
	    backface(TRUE);

	    if (!rgbp) SetUIColor(UIGRAY62);

	    expBounds[0] = bounds[0] - frameWidth;
	    expBounds[1] = bounds[1] + frameWidth;
	    expBounds[2] = bounds[2] - frameWidth;
	    expBounds[3] = bounds[3] + frameWidth;
	    expBounds[4] = bounds[4] - frameWidth;
	    expBounds[5] = bounds[5] + frameWidth;

	    /*-x*/
	    FrameNotchedSpaceRect(
		bounds[0], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[2], bounds[4],
		rgbp ? 1.0 : 0.0, 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		expBounds[0], expBounds[2], expBounds[4],
		expBounds[0], expBounds[2], expBounds[5],
		expBounds[0], expBounds[3], expBounds[5],
		expBounds[0], expBounds[3], expBounds[4],
		rgbp ? -1.0 : 0.0, 0.0, 0.0, frameWidth);

	    /*+x*/
	    FrameNotchedSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[4],
		rgbp ? -1.0 : 0.0, 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		expBounds[1], expBounds[3], expBounds[4],
		expBounds[1], expBounds[3], expBounds[5],
		expBounds[1], expBounds[2], expBounds[5],
		expBounds[1], expBounds[2], expBounds[4],
		rgbp ? 1.0 : 0.0, 0.0, 0.0, frameWidth);

	    if (!rgbp) SetUIColor(UIGRAY25);

	    /*-y*/
	    FrameNotchedSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, rgbp ? 1.0 : 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		expBounds[1], expBounds[2], expBounds[5],
		expBounds[0], expBounds[2], expBounds[5],
		expBounds[0], expBounds[2], expBounds[4],
		expBounds[1], expBounds[2], expBounds[4],
		0.0, rgbp ? -1.0 : 0.0, 0.0, frameWidth);

	    /*+y*/
	    FrameNotchedSpaceRect(
		bounds[1], bounds[3], bounds[4],
		bounds[1], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[4],
		0.0, rgbp ? -1.0 : 0.0, 0.0, frameWidth);
	    FrameWideSpaceRect(
		expBounds[0], expBounds[3], expBounds[4],
		expBounds[0], expBounds[3], expBounds[5],
		expBounds[1], expBounds[3], expBounds[5],
		expBounds[1], expBounds[3], expBounds[4],
		0.0, rgbp ? 1.0 : 0.0, 0.0, frameWidth);

	    if (!rgbp) SetUIColor(UIGRAY12);

	    /*floor*/
	    FrameNotchedSpaceRect(
		bounds[0], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[4],
		0.0, 0.0, rgbp ? 1.0 : 0.0, frameWidth);
	    FrameWideSpaceRect(
		expBounds[0], expBounds[3], expBounds[4],
		expBounds[1], expBounds[3], expBounds[4],
		expBounds[1], expBounds[2], expBounds[4],
		expBounds[0], expBounds[2], expBounds[4],
		0.0, 0.0, rgbp ? -1.0 : 0.0, frameWidth);

	    /*ceiling*/
	    FrameNotchedSpaceRect(
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, 0.0, rgbp ? -1.0 : 0.0, frameWidth);
	    FrameWideSpaceRect(
		expBounds[1], expBounds[2], expBounds[5],
		expBounds[1], expBounds[3], expBounds[5],
		expBounds[0], expBounds[3], expBounds[5],
		expBounds[0], expBounds[2], expBounds[5],
		0.0, 0.0, rgbp ? 1.0 : 0.0, frameWidth);

	    backface(FALSE);
	}
	OptimizeSharpness();
    }
#endif
    return ObjTrue;
}

static ObjPtr MakeAxesMajorStep(object)
ObjPtr object;
/*Makes the axis major steps of an object*/
{
    double maxSize;
    real bounds[6];
    ObjPtr var;
    real *elements;
    double majorWidth;
    int nTics;

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

    CalcGoodSteps(maxSize, 100, 10, &majorWidth, &nTics);

    var = NewRealArray(1, 3L);
    elements = ELEMENTS(var);
    elements[0] = majorWidth;
    elements[1] = majorWidth;
    elements[2] = majorWidth;
    SetVar(object, AXISMAJORSTEP, var);
    return ObjTrue;
}

static ObjPtr MakeAxesDivisions(object)
ObjPtr object;
/*Makes the axis divisions of an object*/
{
    double maxSize;
    real bounds[6];
    ObjPtr var;
    real *elements;
    double majorWidth;
    int nTics;

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

    CalcGoodSteps(maxSize, 100, 10, &majorWidth, &nTics);

    var = NewRealArray(1, 3L);
    elements = ELEMENTS(var);
    elements[0] = nTics;
    elements[1] = nTics;
    elements[2] = nTics;
    SetVar(object, AXISDIVISIONS, var);
    return ObjTrue;
}


static ObjPtr DrawWalls(object)
ObjPtr object;
/*Draw walls around a bounded object.  No, that's done in VisObj*/
{
    return ObjTrue;
}

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

    MakeVar(object, SURFACE);
    surface = GetVar(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*/
	ObjPtr cPalette;

	MakeVar(object, CPALETTE);
	cPalette = GetPaletteVar("MakePicColored", object, CPALETTE);
	if (cPalette)
	{
	    SetPalette(cPalette);
	    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;
    ObjPtr cPalette;

    MakeVar(object, CPALETTE);
    cPalette = GetPaletteVar("MakeGeoPicColored", object, CPALETTE);
    if (cPalette)
    {
	SetPalette(cPalette);
    }

    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, var;
	    /*Color it by itself*/

	    SetCurField(FIELD1, colorObj);
	    data = curFields[FIELD1] . objectInfo;

	    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 ((!drawSolid) && 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, VISMATERIAL, matIndex, material);
	    if (rgbp)
	    {
		lmbind(MATERIAL, VISMATERIAL);
	    }

	    if ((!drawSolid) && GetPredicate(object, ISTRANSLUCENT))
	    {
		BeginTranslucent();
	    }

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

	    OptimizeColor();

	    /*Make sure it's colored*/
	    MakeVar(object, PICCOLORED);

	    drawingQuality = DQ_FULL;
	    DrawPicture(surface, drawingTransparent, lightShading, colorShading, colors);

	    OptimizeSharpness();

	    if (GetPredicate(object, TWOSIDEDSURFACE))
	    {
		TwoSided(false);
	    }
	    if ((!drawSolid) && GetPredicate(object, ISTRANSLUCENT))
	    {
		EndTranslucent();
	    }

	    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;
	}

	/*Make sure it's colored*/
	MakeVar(object, PICCOLORED);

	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);
	}
	/*Make sure it's colored*/
	MakeVar(object, PICCOLORED);

	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;
    ObjPtr picture;
    ObjPtr newPic;
    ObjPtr data;
    ObjPtr eternalData;
    long flags;
    ObjPtr var;

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

    SetCurField(FIELD1, repObj);

    flags = GetDatasetInfo(repObj);
    if (flags & DS_HASGEOMETRY)
    {
	picture = curFields[FIELD1] . objectInfo;

	eternalData = GetVar(repObj, ETERNALPART);

	if (eternalData)
	{
	    newPic = ConvertPicture(eternalData, object);
	    if (picture)
	    {
		ConvertOntoPicture(newPic, picture, object);
	    }
	    SetVar(object, SURFACE, newPic);
	}
	else
	{
	    if (picture)
	    {
		SetVar(object, SURFACE, newPic = ConvertPicture(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;
	}
	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 HAVE_PROTOTYPES
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 HAVE_PROTOTYPES
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 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)
    {
	SetVar(geometry, SPHERESUBDIV, NewInt(2));
    }

    AssocDirectControlWithVar(radio, geometry, SPHERESUBDIV);
    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)
    {
	SetVar(geometry, FRUSTUMSUBDIV, NewInt(2));
    }

    AssocDirectControlWithVar(radio, geometry, FRUSTUMSUBDIV);
    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, PARENT, panelContents);
    PrefixList(panelContents, button);
    if (!GetVar(geometry, CAPENDSP)) SetVar(geometry, CAPENDSP, NewInt(1));
    AssocDirectControlWithVar(button, geometry, CAPENDSP);
    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 HAVE_PROTOTYPES
void ScaleAroundPoint(real sx, real sy, real sz, real px, real py, real pz)
#else
void ScaleAroundPoint(sx, sy, sz, px, py, pz)
real sx; real sy; real sz; real px; real py; real pz;
#endif
/*Scales subsequent drawing around a point*/
{
#ifdef GRAPHICS
    Matrix m;

	PushTransformation();
	loadmatrix(Identity);
	translate(px, py, pz);
	getmatrix(m);
	PopTransformation();
	multmatrix(m);

	PushTransformation();
	loadmatrix(Identity);
	scale(sx, sy, sz);
	getmatrix(m);
	PopTransformation();
	multmatrix(m);

	PushTransformation();
	loadmatrix(Identity);
	translate(-px, -py, -pz);
	getmatrix(m);
	PopTransformation();
	multmatrix(m);
#endif
}

#ifdef HAVE_PROTOTYPES
void DrawPanelLines(real x1, real y1, real z1,
		    real x2, real y2, real z2,
		    char axis, real min, real max, real step)
#else
void DrawPanelLines( x1,  y1,  z1,
		     x2,  y2,  z2,
		     axis,  min,  max,  step)
real x1, y1, z1;
real x2, y2, z2;
char axis;
real min, max, step;
#endif
/*Draws a bunch of lines from min to max by step along axis (x, y, z)
  using [xyz]1 and [xyz]2 as a basis.*/
{
    real cur;

    if (min > max) {cur = max; max = min; min = cur;}

    cur = rceil(min / step) * step;

    while (cur <= max)
    {
	if (cur >= min)
	{
	    switch(axis)
	    {
		case 'x':
		    x1 = x2 = cur;
		    break;
		case 'y':
		    y1 = y2 = cur;
		    break;
		case 'z':
		    z1 = z2 = cur;
		    break;
	    }
	    DrawSpaceLine(x1, y1, z1, x2, y2, z2);
	}

	cur += step;
    }
}

#ifdef HAVE_PROTOTYPES
void DrawVisObject(ObjPtr object)
#else
void DrawVisObject(object)
ObjPtr object;
#endif
/*Draws a visualization object*/
{
    ObjPtr var;
    float xScale, yScale, zScale;
    real bounds[6];
    int panelFlags, shadowFlags, mirrorFlags;
    int oldMinDrawingQuality;
    Bool drawBackground;
    Bool antiAlias = false;	/*True iff antialiased lines*/

    drawBackground = GetPredicate(object, DRAWBACKGROUND);

    /*Get bounds*/
    GetBounds(object, bounds);

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

    PushTransformation();

    orderReversed = false;

    /*Scale the object*/
    ScaleAroundPoint(xScale, yScale, zScale, bounds[0], bounds[2], bounds[4]);
    if (xScale < 0.0) orderReversed = orderReversed ? false : true;
    if (yScale < 0.0) orderReversed = orderReversed ? false : true;
    if (zScale < 0.0) orderReversed = orderReversed ? false : true;

    /*Draw the main object*/
    DrawObject(object);

    oldMinDrawingQuality = minDrawingQuality;
    minDrawingQuality = DQ_WALLS + 1;

    /*Scale bounds*/
    bounds[1] = bounds[0] + (bounds[1] - bounds[0]) * xScale;
    bounds[3] = bounds[2] + (bounds[3] - bounds[2]) * xScale;
    bounds[5] = bounds[4] + (bounds[5] - bounds[4]) * xScale;

    /*Draw the panels, if appropriate*/
    var = GetVar(object, WALLPANELFLAGS);
    if (var && maxDrawingQuality >= DQ_WALLS && !drawingTransparent)
    {
	panelFlags = GetInt(var);
    }
    else
    {
	panelFlags = 0;
    }

    /*See if there are any mirrors*/
    var = GetVar(object, WALLMIRRORFLAGS);
    if (var && maxDrawingQuality >= DQ_WALLS)
    {
	mirrorFlags = GetInt(var);
    }
    else
    {
	mirrorFlags = 0;
    }

    /*See if there are any shadows*/
    var = GetVar(object, WALLSHADOWFLAGS);
    if (var && maxDrawingQuality >= DQ_WALLS && !drawingTransparent)
    {
	shadowFlags = GetInt(var);
    }
    else
    {
	shadowFlags = 0;
    }

    /*Draw reflections through mirrors*/
    if (mirrorFlags)
    {
	/*First order reflections*/
	orderReversed = orderReversed ? false : true;
	if (mirrorFlags & 1)	/*-X*/
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, 1.0, 1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if (mirrorFlags & 2)	/*+X*/
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, 1.0, 1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if (mirrorFlags & 4)	/*-Y*/
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, -1.0, 1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if (mirrorFlags & 8)	/*+Y*/
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, -1.0, 1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if (mirrorFlags & 16)	/*-Z*/
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, 1.0, -1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if (mirrorFlags & 32)	/*+Z*/
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, 1.0, -1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}

	/*Second order reflections*/
	orderReversed = orderReversed ? false : true;
	if ((mirrorFlags & 1) && (mirrorFlags & 4))		/* -X, -Y */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, 1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 1) && (mirrorFlags & 8))		/* -X, +Y */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, 1.0, bounds[0], bounds[3], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 4))		/* +X, -Y */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, 1.0, bounds[1], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 8))		/* +X, +Y */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, 1.0, bounds[1], bounds[3], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 1) && (mirrorFlags & 16))	/* -X, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, 1.0, -1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 1) && (mirrorFlags & 32))	/* -X, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, 1.0, -1.0, bounds[0], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 16))	/* +X, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, 1.0, -1.0, bounds[1], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 32))	/* +X, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, 1.0, -1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 4) && (mirrorFlags & 16))	/* -Y, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, -1.0, -1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 4) && (mirrorFlags & 32))	/* -Y, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, -1.0, -1.0, bounds[0], bounds[2], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 8) && (mirrorFlags & 16))	/* +Y, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, -1.0, -1.0, bounds[1], bounds[3], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 8) && (mirrorFlags & 32))	/* +Y, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(1.0, -1.0, -1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}

	/*Third order reflections*/
	orderReversed = orderReversed ? false : true;
	if ((mirrorFlags & 1) && (mirrorFlags & 4) && (mirrorFlags & 16))	/* -X, -Y, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 1) && (mirrorFlags & 4) && (mirrorFlags & 32))	/* -X, -Y, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[0], bounds[2], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 1) && (mirrorFlags & 4) && (mirrorFlags & 16))	/* -X, +Y, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[0], bounds[3], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 1) && (mirrorFlags & 4) && (mirrorFlags & 32))	/* -X, +Y, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[0], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 4) && (mirrorFlags & 16))	/* +X, -Y, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[1], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 4) && (mirrorFlags & 32))	/* +X, -Y, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[1], bounds[2], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 4) && (mirrorFlags & 16))	/* +X, +Y, -Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[1], bounds[3], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	}
	if ((mirrorFlags & 2) && (mirrorFlags & 4) && (mirrorFlags & 32))	/* +X, +Y, +Z */
	{
	    PushTransformation();
	    ScaleAroundPoint(-1.0, -1.0, -1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	}

	/*Back to original order*/
	orderReversed = orderReversed ? false : true;
    }

    /*Draw the panels with no Z-buffering*/
    if (panelFlags && drawBackground)
    {
	int matIndex;

	BeginMask(true, true, true, false);

	backface(TRUE);

	if (rgbp)
	{
	    real *elements;
	    var = GetVar(object, WALLPANELCOLOR);
	    if (var)
	    {
		elements = ELEMENTS(var);
	    }
	    else
	    {
		elements = 0;
	    }

	    matIndex = 0;
	    material[matIndex++] = DIFFUSE;
	    material[matIndex++] = elements ? elements[0] : 0.5;
	    material[matIndex++] = elements ? elements[1] : 0.5;
	    material[matIndex++] = elements ? elements[2] : 0.5;
	    material[matIndex++] = LMNULL;
	    lmdef(DEFMATERIAL, VISMATERIAL, matIndex, material);
	    lmbind(MATERIAL, VISMATERIAL);
	    lmcolor(LMC_DIFFUSE);
	}

	OptimizeColor();

	if (!rgbp) SetUIColor(UIGRAY12);

	if (panelFlags & 1)
	{
	    /*-x*/
	    FillSpaceRect(
		bounds[0], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[2], bounds[4],
		rgbp ? 1.0 : 0.0, 0.0, 0.0);
	}
	if (panelFlags & 2)
	{
	    /*+x*/
	    FillSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[4],
		rgbp ? -1.0 : 0.0, 0.0, 0.0);
	}

	if (!rgbp) SetUIColor(UIGRAY25);

	if (panelFlags & 4)
	{
	    /*-y*/
	    FillSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, rgbp ? 1.0 : 0.0, 0.0);
	}
	if (panelFlags & 8)
	{
	    /*+y*/
	    FillSpaceRect(
		bounds[1], bounds[3], bounds[4],
		bounds[1], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[4],
		0.0, rgbp ? -1.0 : 0.0, 0.0);
	}

	if (!rgbp) SetUIColor(UIGRAY25);

	if (panelFlags & 16)
	{
	    /*floor*/
	    FillSpaceRect(
		bounds[0], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[4],
		0.0, 0.0, rgbp ? 1.0 : 0.0);
	}
	if (panelFlags & 32)
	{
	    /*ceiling*/
	    FillSpaceRect(
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, 0.0, rgbp ? -1.0 : 0.0);
	}

	OptimizeSharpness();

	if (rgbp)
	{
	    lmcolor(LMC_COLOR);
	}

	backface(FALSE);

	EndMask();
    }


    /*Draw shadows on panels*/
    if (shadowFlags)
    {
	drawSolid = true;
	SetUIColor(UIBLACK);
	OverrideColor(true);

	if ((shadowFlags & 1) && (observerPosition[0] > bounds[0]))	/*-X*/
	{
	    if ((panelFlags & 1) && drawBackground)
	    {
		BeginMask(true, true, true, false);
	    }
	    PushTransformation();
	    ScaleAroundPoint(0.0001, 1.0, 1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	    if ((panelFlags & 1) && drawBackground)
	    {
		EndMask();
	    }
	}
	if ((shadowFlags & 2) && (observerPosition[0] < bounds[1]))	/*+X*/
	{
	    if ((panelFlags & 2) && drawBackground)
	    {
		BeginMask(true, true, true, false);
	    }
	    PushTransformation();
	    ScaleAroundPoint(0.0001, 1.0, 1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	    if ((panelFlags & 2) && drawBackground)
	    {
		EndMask();
	    }
	}
	if ((shadowFlags & 4) && (observerPosition[1] > bounds[2]))	/*-Y*/
	{
	    if ((panelFlags & 4) && drawBackground)
	    {
		BeginMask(true, true, true, false);
	    }
	    PushTransformation();
	    ScaleAroundPoint(1.0, 0.0001, 1.0, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	    if ((panelFlags & 4)  && drawBackground)
	    {
		EndMask();
	    }
	}
	if ((shadowFlags & 8) && (observerPosition[1] < bounds[3]))	/*+Y*/
	{
	    if ((panelFlags & 8)  && drawBackground)
	    {
		BeginMask(true, true, true, false);
	    }
	    PushTransformation();
	    ScaleAroundPoint(1.0, 0.0001, 1.0, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	    if ((panelFlags & 8)  && drawBackground)
	    {
		EndMask();
	    }
	}
	if ((shadowFlags & 16) && (observerPosition[2] > bounds[4]))	/*-Z*/
	{
	    if ((panelFlags & 16)  && drawBackground)
	    {
		BeginMask(true, true, true, false);
	    }
	    PushTransformation();
	    ScaleAroundPoint(1.0, 1.0, 0.0001, bounds[0], bounds[2], bounds[4]);
	    DrawObject(object);
	    PopTransformation();
	    if ((panelFlags & 16) && drawBackground)
	    {
		EndMask();
	    }
	}
	if ((shadowFlags & 32) && (observerPosition[2] < bounds[5]))	/*+Z*/
	{
	    if ((panelFlags & 32) && drawBackground)
	    {
		BeginMask(true, true, true, false);
	    }
	    PushTransformation();
	    ScaleAroundPoint(1.0, 1.0, 0.0001, bounds[1], bounds[3], bounds[5]);
	    DrawObject(object);
	    PopTransformation();
	    if ((panelFlags & 32)  && drawBackground)
	    {
		EndMask();
	    }
	}
	OverrideColor(false);
	drawSolid = false;
    }

    /*Draw lines over shadow*/
    if (panelFlags)
    {
	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;
	}

	if (GetPredicate(object, DRAWOUTLINE))
	{
	    /*Draw the outline*/

	    var = GetVar(object, WALLLINESCOLOR);
	    if (var)
	    {
		SetObjectColor(var);
	    }
	    else
	    {
		SetUIColor(UIGRAY75);
	    }

	    if (panelFlags & 1)
	    {
		/*-x*/
		FrameSpaceRect(
		    bounds[0], bounds[3], bounds[4],
		    bounds[0], bounds[3], bounds[5],
		    bounds[0], bounds[2], bounds[5],
		    bounds[0], bounds[2], bounds[4]);
	    }
	    if (panelFlags & 2)
	    {
		/*+x*/
		FrameSpaceRect(
		    bounds[1], bounds[2], bounds[4],
		    bounds[1], bounds[2], bounds[5],
		    bounds[1], bounds[3], bounds[5],
		    bounds[1], bounds[3], bounds[4]);
	    }

	    if (!rgbp) SetUIColor(UIGRAY25);

	    if (panelFlags & 4)
	    {
		/*-y*/
		FrameSpaceRect(
		    bounds[1], bounds[2], bounds[4],
		    bounds[0], bounds[2], bounds[4],
		    bounds[0], bounds[2], bounds[5],
		    bounds[1], bounds[2], bounds[5]);
	    }
	    if (panelFlags & 8)
	    {
		/*+y*/
		FrameSpaceRect(
		    bounds[1], bounds[3], bounds[4],
		    bounds[1], bounds[3], bounds[5],
		    bounds[0], bounds[3], bounds[5],
		    bounds[0], bounds[3], bounds[4]);
	    }

	    if (!rgbp) SetUIColor(UIGRAY25);

	    if (panelFlags & 16)
	    {
		/*floor*/
		FrameSpaceRect(
		    bounds[0], bounds[2], bounds[4],
		    bounds[1], bounds[2], bounds[4],
		    bounds[1], bounds[3], bounds[4],
		    bounds[0], bounds[3], bounds[4]);
	    }
	    if (panelFlags & 32)
	    {
		/*ceiling*/
		FrameSpaceRect(
		    bounds[0], bounds[2], bounds[5],
		    bounds[0], bounds[3], bounds[5],
		    bounds[1], bounds[3], bounds[5],
		    bounds[1], bounds[2], bounds[5]);
	    }
	}

	if (GetPredicate(object, DRAWGRID))
	{
	    /*Draw the grid*/
	    double xSpace, ySpace, zSpace;
	    real majorStep[3];
	    real maxSize;
	    int nTics;

	    MakeVar(object, AXISMAJORSTEP);
	    var = GetFixedArrayVar("DrawVisObject", object, AXISMAJORSTEP, 1, 3L);
	    if (var)
	    {
		Array2CArray(majorStep, var);
	    }
	    else
	    {
		majorStep[0] = majorStep[1] = majorStep[2] = 1.0;
	    }

	    /*Do the x, y, and z axes*/
	    xSpace = majorStep[0];
	    ySpace = majorStep[1];
	    zSpace = majorStep[2];

	    var = GetVar(object, WALLLINESCOLOR);
	    if (var)
	    {
		SetObjectColor(var);
	    }
	    else
	    {
		SetUIColor(UIGRAY75);
	    }

	    if (panelFlags & 1)
	    {
		/*-x*/
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[4],
		    bounds[0], bounds[3], bounds[4],
		    'z', bounds[4], bounds[5], zSpace);
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[4],
		    bounds[0], bounds[2], bounds[5],
		    'y', bounds[2], bounds[3], ySpace);
	    }

	    if (panelFlags & 2)
	    {
		/*+x*/
		DrawPanelLines(
		    bounds[1], bounds[2], bounds[4],
		    bounds[1], bounds[3], bounds[4],
		    'z', bounds[4], bounds[5], zSpace);
		DrawPanelLines(
		    bounds[1], bounds[2], bounds[4],
		    bounds[1], bounds[2], bounds[5],
		    'y', bounds[2], bounds[3], ySpace);
	    }

	    if (panelFlags & 4)
	    {
		/*-y*/
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[4],
		    bounds[1], bounds[2], bounds[4],
		    'z', bounds[4], bounds[5], zSpace);
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[4],
		    bounds[0], bounds[2], bounds[5],
		    'x', bounds[0], bounds[1], ySpace);
	    }
	    if (panelFlags & 8)
	    {
		/*+y*/
		DrawPanelLines(
		    bounds[0], bounds[3], bounds[4],
		    bounds[1], bounds[3], bounds[4],
		    'z', bounds[4], bounds[5], zSpace);
		DrawPanelLines(
		    bounds[0], bounds[3], bounds[4],
		    bounds[0], bounds[3], bounds[5],
		    'x', bounds[0], bounds[1], ySpace);
	    }

	    if (!rgbp) SetUIColor(UIGRAY25);

	    if (panelFlags & 16)
	    {
		/*floor*/
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[4],
		    bounds[0], bounds[3], bounds[4],
		    'x', bounds[0], bounds[1], xSpace);
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[4],
		    bounds[1], bounds[2], bounds[4],
		    'y', bounds[2], bounds[3], ySpace);
	    }
	    if (panelFlags & 32)
	    {
		/*ceiling*/
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[5],
		    bounds[0], bounds[3], bounds[5],
		    'x', bounds[0], bounds[1], xSpace);
		DrawPanelLines(
		    bounds[0], bounds[2], bounds[5],
		    bounds[1], bounds[2], bounds[5],
		    'y', bounds[2], bounds[3], ySpace);
	    }
	}
	SetLineWidth(1);
	if (antiAlias)
	{
	    linesmooth(SML_OFF);
	    blendfunction(BF_ONE, BF_ZERO);
	    subpixel(FALSE);
	}
    }

    /*Draw the panels with only Z-buffering, no drawing*/
    if (panelFlags && drawBackground)
    {
	int matIndex;

	BeginMask(false, false, false, true);

	backface(TRUE);
	if (panelFlags & 1)
	{
	    /*-x*/
	    FillSpaceRect(
		bounds[0], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[2], bounds[4],
		rgbp ? 1.0 : 0.0, 0.0, 0.0);
	}
	if (panelFlags & 2)
	{
	    /*+x*/
	    FillSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[4],
		rgbp ? -1.0 : 0.0, 0.0, 0.0);
	}
	if (panelFlags & 4)
	{
	    /*-y*/
	    FillSpaceRect(
		bounds[1], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[4],
		bounds[0], bounds[2], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, rgbp ? 1.0 : 0.0, 0.0);
	}
	if (panelFlags & 8)
	{
	    /*+y*/
	    FillSpaceRect(
		bounds[1], bounds[3], bounds[4],
		bounds[1], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[0], bounds[3], bounds[4],
		0.0, rgbp ? -1.0 : 0.0, 0.0);
	}
	if (panelFlags & 16)
	{
	    /*floor*/
	    FillSpaceRect(
		bounds[0], bounds[2], bounds[4],
		bounds[1], bounds[2], bounds[4],
		bounds[1], bounds[3], bounds[4],
		bounds[0], bounds[3], bounds[4],
		0.0, 0.0, rgbp ? 1.0 : 0.0);
	}
	if (panelFlags & 32)
	{
	    /*ceiling*/
	    FillSpaceRect(
		bounds[0], bounds[2], bounds[5],
		bounds[0], bounds[3], bounds[5],
		bounds[1], bounds[3], bounds[5],
		bounds[1], bounds[2], bounds[5],
		0.0, 0.0, rgbp ? -1.0 : 0.0);
	}

	backface(FALSE);

	EndMask();
    }

    PopTransformation();

    minDrawingQuality = oldMinDrawingQuality;
}

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);
    MakeVar(colorObj, CPALETTE);
    if (GetPredicate(colorObj, COLORBYSELF) && 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"));
    }
}

static ObjPtr MakeVisAppearance(object)
ObjPtr object;
/*Makes a vis object's appearance*/
{
    SetVar(object, APPEARANCE, ObjTrue);
    ImInvalid(object);
}

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

    if (!IsDataset(object))
    {
	object = GetVar(object, MAINDATASET);
    }
    if (!IsDataset(object))
    {
	WarnUser(CW_CANNOTDROPNONDATASET);
	return ObjFalse;
    }

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

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

    info = GetDatasetInfo(fieldObj);
    if ((info & DS_HASGEOMETRY))
    {
	WarnUser(CW_NOGEOMETRYERROR);
	return ObjFalse;
    }

    /*Create an icon for it*/
    name = GetStringVar("DropInSizeCorral", 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 ball 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 sized object*/
    SetVar(visObj, SIZEOBJ, fieldObj);

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

static ObjPtr AddSizedControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls to a sized object*/
{
    ObjPtr control, button, checkBox, corral, sw, textBox, slider, icon, radioGroup;
    ObjPtr sizeObj, 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("AddSizedControls", 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 size 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("Size Field"));
    PrefixList(panelContents, corral);
    SetVar(corral, HELPSTRING,
	NewString("This corral shows the dataset that is used to \
determine the size of 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, DropInSizeCorral);

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

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

    /*Create the constant size control*/
    var = GetRealVar("AddSizedControls", object, SIZECONSTANT);
    if (!var)
    {
	SetVar(object, SIZECONSTANT, NewReal(0.0));
    }
    textBox = NewTextBox(MAJORBORDER, left - MINORBORDER, 
			m1 - EDITBOXHEIGHT / 2,
			m1 + EDITBOXHEIGHT / 2, 
			EDITABLE + WITH_PIT + ONE_LINE,
			"Fixed Size", "");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetVar(textBox, HELPSTRING, NewString("This text box gives a constant \
size when it is selected by the switch."));
    SetVar(textBox, WHICHVAR, NewSymbol(SIZECONSTANT));
    SetTextAlign(textBox, RIGHTALIGN);
    AssocTextRealControlWithVar(textBox, object, SIZECONSTANT, 0.0, plusInf, TR_NE_TOP);

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

    /*Create the choice switch*/
    var = GetIntVar("AddSizedControls", object, SIZESWITCH);
    if (!var)
    {
	SetVar(object, SIZESWITCH, NewInt(0));
    }
    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,
			"Size Switch");
    PrefixList(panelContents, sw);
    SetVar(sw, PARENT, panelContents);
    SetVar(sw, REPOBJ, object);
    SetVar(sw, HELPSTRING, NewString("This switch controls whether the \
soze of the visualization object comes from a \
field or from a fixed size."));
    AssocDirectControlWithVar(sw, object, SIZESWITCH);

    left += SWITCHWIDTH + MINORBORDER;

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

    /*Create the size factor*/
    var = GetVar(object, SIZEFACTOR);
    if (!var)
    {
	SetVar(object, SIZEFACTOR, NewReal(0.0));
    }
    right = left + SIZEEDITWIDTH;
    textBox = TemplateTextBox(VisSizeTemplate, "Size Factor", 
			EDITABLE + WITH_PIT + ONE_LINE,
			"");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetVar(textBox, HELPSTRING, NewString("This text box gives a multiplier \
for the size provided by the switch."));
    SetVar(textBox, WHICHVAR, NewSymbol(SIZEFACTOR));
    AssocTextRealControlWithVar(textBox, object, SIZEFACTOR, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
    SetTextAlign(textBox, RIGHTALIGN);

    /*Create the text box*/
    textBox = TemplateTextBox(VisSizeTemplate, "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(VisSizeTemplate, "Plus", 0, "+");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextFont(textBox, "Courier-Bold");
    SetTextSize(textBox, 18);
    left = right + MINORBORDER;

    /*Create the size offset*/
    var = GetRealVar("AddSizedControls", object, SIZEOFFSET);
    if (!var)
    {
	SetVar(object, SIZEOFFSET, NewReal(0.0));
    }
    right = left + SIZEEDITWIDTH;
    textBox = TemplateTextBox(VisSizeTemplate, "Size Offset", 
			EDITABLE + WITH_PIT + ONE_LINE, "");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetVar(textBox, HELPSTRING, NewString("This text box gives an offset \
for the size provided by the switch, multiplied by the multiplier."));
    AssocTextRealControlWithVar(textBox, object, SIZECONSTANT, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
    SetTextAlign(textBox, RIGHTALIGN);

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

    return ObjTrue;
}

Bool cacheMade;		/*Global to indicate cache was made*/

static ObjPtr MakeCachedOpaque(object)
ObjPtr object;
/*Routine to make opaque cache*/
{
    SetVar(object, CACHEDOPAQUE, GetVar(object, CACHEDOPAQUE));
    cacheMade = true;
    return ObjTrue;
}

static ObjPtr MakeCachedTransparent(object)
ObjPtr object;
/*Routine to make transparent cache*/
{
    SetVar(object, CACHEDTRANSPARENT, GetVar(object, CACHEDTRANSPARENT));
    cacheMade = true;
    return ObjTrue;
}

void InitVisObjects()
/*Initializes the visualization objects.*/
{
    ObjPtr icon;
    ObjPtr array, var;
    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);
    AddSnapVar(visClass, HIDDEN);
    SetMethod(visClass, CLONE, CloneVisObject);
    SetMethod(visClass, PREFIXDATASETS, PrefixMainDataset);
    SetMethod(visClass, DELETE, DeleteObject);
    SetMethod(visClass, DUPLICATE, DuplicateSpaceObject);
    SetMethod(visClass, LOCALCOPY, MakeLocalCopy);
    SetMethod(visClass, VISUALIZE, VisualizeVisObject);
    SetMethod(visClass, APPEARANCE, MakeVisAppearance);
    SetVar(visClass, XSCALE, NewReal(1.0));
    SetVar(visClass, YSCALE, NewReal(1.0));
    SetVar(visClass, ZSCALE, NewReal(1.0));

    
    visAxes = NewObject(visClass, 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);
    SetVar(icon, PANELHELP, NewString("This panel contains controls that \
let you change the name of the axes and display the axes in various ways."));
    SetMethod(visAxes, ADDCONTROLS, AddAxesControls);
    SetMethod(visAxes, AXISMAJORSTEP, MakeAxesMajorStep);
    SetMethod(visAxes, AXISDIVISIONS, MakeAxesDivisions);
    SetVar(visAxes, DRAWNAMES, NewInt(0));

    visBounded = NewObject(visAxes, 0);
    AddToReferenceList(visBounded);
    SetMethod(visBounded, DRAW, DrawBounded);
    SetMethod(visBounded, ADDCONTROLS, AddBoundedControls);
    SetMethod(visBounded, BOUNDS, MakeFormBounds);
    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);
    SetVar(icon, PANELHELP, NewString("This panel lets you change the bounds \
around a visualization object and lets you decorate the bounds with an outline \
box."));
    SetMethod(visBounded, XNAME, MakeBoundedXName);
    SetMethod(visBounded, YNAME, MakeBoundedYName);
    SetMethod(visBounded, ZNAME, MakeBoundedZName);
    SetVar(visBounded, BOUNDSBOX, NewInt(0));

    visWalls = NewObject(visBounded, 0);
    AddToReferenceList(visWalls);
    SetMethod(visWalls, DRAW, DrawWalls);
    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);
    SetVar(icon, PANELHELP, NewString("There are six walls around every \
visualization object.  This control panel lets you display these walls.  The series of \
check boxes at the top allow you to turn on individual walls.  The Wall check boxes \
display the walls themselves.  The Shadow check boxes determine whether the \
visualization object casts a shadow on each wall.  The shadows are intended to \
be used to see projections of the visualization objects along an axis and are \
not affected by lighting.  The Mirror check boxes determine whether the visualization \
object is to be mirrored on the other side of the wall.\n\n\
The controls at the bottom control how each wall is displayed when its Wall button \
is on."));
    SetMethod(visWalls, ADDCONTROLS, AddWallsControls);
    SetVar(visWalls, DRAWBACKGROUND, ObjTrue);
    SetVar(visWalls, DRAWOUTLINE, ObjFalse);
    SetVar(visWalls, DRAWGRID, ObjFalse);
    array = NewRealArray(1, 3L);
    elements = ELEMENTS(array);
    elements[0] = 0.5;
    elements[1] = 0.5;
    elements[2] = 0.5;
    SetVar(visWalls, WALLPANELCOLOR, array);
    array = NewRealArray(1, 3L);
    elements = ELEMENTS(array);
    elements[0] = 0.75;
    elements[1] = 0.75;
    elements[2] = 0.75;
    SetVar(visWalls, WALLLINESCOLOR, array);
    
    /*Class for any colored object*/
    visColored = NewObject(visWalls, 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);
    SetVar(icon, PANELHELP, NewString("This panel lets you control how a \
visualization is colored.  At the left is a miniature dataflow diagram which \
specifies that the basic color of an object comes from either a fixed color \
or from a color field.  You can drag another field into the icon corral to change \
the field used to color the object.  A control called a switch which looks like \
two lines coming into an arrow controls which is used.  Click on the section of \
the switch to turn on.  The color is passed through a brightness control, giving the \
final color.  At the bottom left are controls for shading and transparency."));
    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);
    DeclareDependency(visColored, CACHEDOPAQUE, PICCOLORED);
    DeclareDependency(visColored, CACHEDTRANSPARENT, PICCOLORED);
    SetVar(visColored, COLORS, NewInt(0));
    DeclareDependency(visColored, APPEARANCE, PICCOLORED);
    DeclareIndirectDependency(visColored, PICCOLORED, CPALETTE, CHANGED);
    SetMethod(visColored, PICCOLORED, MakePicColored);
    var = NewRealArray(1, 3L);
    elements = ELEMENTS(var);
    elements[0] = elements[1] = elements[2] = 1.0;
    SetVar(visColored, BASECOLOR, var);
    SetVar(visColored, BRIGHTNESS, NewReal(1.0));
    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);
    DeclareIndirectDependency(visColored, CPALETTE, COLOROBJ, CPALETTE);
    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);
    SetVar(icon, PANELHELP, NewString("This control panel lets you show dots in the visualization \
object.  The appearance of the dots depends on the visualization object in use."));
    SetMethod(visDots, PICKPOINT, PickVisDots);
    SetMethod(visDots, DRAW, DrawVisDots);
    DeclareDependency(visDots, SURFACE, TIME);
    DeclareDependency(visDots, PICCOLORED, SURFACE);
    DeclareDependency(visDots, APPEARANCE, SURFACE);
    DeclareDependency(visDots, SURFACE, MAINDATASET);
    DeclareIndirectDependency(visDots, SURFACE, MAINDATASET, CHANGED);
    SetVar(visDots, CACHEGRAPHICS, ObjFalse);
    DeclareDependency(visDots, CACHEDOPAQUE, SURFACE);
    DeclareDependency(visDots, CACHEDOPAQUE, CACHEGRAPHICS);
    DeclareDependency(visDots, CACHEDTRANSPARENT, SURFACE);
    DeclareDependency(visDots, CACHEDTRANSPARENT, CACHEGRAPHICS);
    SetMethod(visDots, CACHEDOPAQUE, MakeCachedOpaque);
    SetMethod(visDots, CACHEDTRANSPARENT, MakeCachedTransparent);
 
    /*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);
    SetVar(icon, PANELHELP, NewString("This control panel allows you to edit \
the parameters used to draw the lines in the visualizaton object and also allows \
you to draw the visualization object as a wire frame.\n"));
    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."));
    SetVar(icon, PANELHELP, NewString("This panel allows you to change the parameters \
used to draw surfaces in the visualization object."));
    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));
    SetMethod(visSurface, DRAW, DrawVisSurface);
    var = NewRealArray(1, 3L);
    elements = ELEMENTS(var);
    elements[0] = elements[1] = elements[2] = 1.0;
    SetVar(visSurface, HIGHLIGHTCOLOR, var);

    /*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(icon, PANELHELP, NewString("This panel lets you control how a \
visualization is deformed.  At the left is a miniature dataflow diagram which \
specifies that the deformation of an object comes from either a fixed deformation \
or from a field.  You can drag another field into the icon corral to change \
the field used to deformed the object.  A control called a switch which looks like \
two lines coming into an arrow controls which is used.  Click on the section of \
the switch to turn on.  The deformation is then multiplied by a factor and added \
to an offset, both of which you can edit.  Objects are deformed by pushing out \
each vertex in the direction of its vertex normal.  To prevent an object from \
being deformed, set it to a fixed deformation of 0 with and offset of 0."));

    SetVar(visDeformed, DEFFACTOR, NewReal(1.0));
    SetVar(visDeformed, DEFCONSTANT, NewReal(0.0));
    SetVar(visDeformed, DEFOFFSET, NewReal(0.0));
    SetVar(visDeformed, DEFORMSWITCH, NewInt(0));
    DeclareIndirectDependency(visDeformed, SURFACE, DEFORMOBJ, CHANGED);
    DeclareDependency(visDeformed, SURFACE, DEFORMOBJ);
    DeclareDependency(visDeformed, SURFACE, DEFCONSTANT);
    DeclareDependency(visDeformed, SURFACE, DEFOFFSET);
    DeclareDependency(visDeformed, SURFACE, DEFFACTOR);
    DeclareDependency(visDeformed, SURFACE, DEFORMSWITCH);
    DeclareDependency(visDeformed, PICDEFORMED, SURFACE);
    SetVar(visDeformed, REVERSESENSE, NewInt(0));
    /*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(icon, HELPSTRING, NewString("Press this button to bring up controls \
for the geometry."));
    SetVar(icon, PANELHELP, NewString("All visualization objects that use \
geometrical shapes, such as spheres and cylinders, must convert the shapes \
to polygons before displaying.  This control panel allows you to specify how \
objects are converted to polygons."));
    SetVar(visGeometryClass, SPHERESUBDIV, NewInt(2));
    SetVar(visGeometryClass, FRUSTUMSUBDIV, NewInt(2));
    SetVar(visGeometryClass, CAPENDSP, ObjTrue);

    DeclareDependency(visGeometryClass, SURFACE, SPHERESUBDIV);
    DeclareDependency(visGeometryClass, SURFACE, FRUSTUMSUBDIV);
    DeclareDependency(visGeometryClass, SURFACE, CAPENDSP);

    /*Class for any object which can be sized*/
    visSized = NewObject(visGeometryClass, 0);
    AddToReferenceList(visSized);
    SetMethod(visSized, ADDCONTROLS, AddSizedControls);
    icon = NewIcon(0, 0, ICONVISSIZE, "Size");
    SetVar(icon, HELPSTRING,
	NewString("Select this icon to show controls to determine \
the size of the elements of the visualization object."));
    SetVar(icon, PANELHELP, NewString("This panel lets you control how a \
visualization is sized.  At the left is a miniature dataflow diagram which \
specifies that the sized of each element in the visualization \
 comes from either a fixed size \
or from a field.  You can drag another field into the icon corral to change \
the field used to deformed the object.  A control called a switch which looks like \
two lines coming into an arrow controls which is used.  Click on the section of \
the switch to turn on."));
    SetVar(visSized, CONTROLICON, icon);

    SetVar(visSized, SIZEFACTOR, NewReal(1.0));
    DeclareIndirectDependency(visSized, SURFACE, SIZEOBJ, CHANGED);
    DeclareDependency(visSized, SURFACE, SIZEOBJ);
    DeclareDependency(visSized, SURFACE, SIZECONSTANT);
    DeclareDependency(visSized, SURFACE, SIZEOFFSET);
    DeclareDependency(visSized, SURFACE, SIZEFACTOR);
    DeclareDependency(visSized, SURFACE, SIZESWITCH);
    SetVar(visSized, SIZESWITCH, NewInt(0));

    /*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);
    SetMethod(geoPictureClass, INITIALIZE, GeometryInit);
    DeclareIndirectDependency(geoPictureClass, SURFACE, REPOBJ, CHANGED);
    SetMethod(geoPictureClass, PICCOLORED, MakeGeoPicColored);

    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);
}

Modified: Sun Nov 17 17:00:00 1996 GMT
Page accessed 2501 times since Sat Apr 17 21:55:08 1999 GMT