/*ScianVisMesh.c
  June 14, 1991
  Eric Pepke
  Routines for mesh visualization object

  The mesh object takes a scalar field defined over a data form with
  topological dimension 2.  The spatial dimension of the dataset may be 
  either 2 or 3.  If the spatial dimension is 2, the Z is assumed to be
  zero.  The mesh visualization object works for regular data forms,
  curvilinear data forms, and nonstructured data forms.

*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianArrays.h"
#include "ScianWindows.h"
#include "ScianTextBoxes.h"
#include "ScianObjWindows.h"
#include "ScianEvents.h"
#include "ScianIcons.h"
#include "ScianColors.h"
#include "ScianControls.h"
#include "ScianLists.h"
#include "ScianSpaces.h"
#include "ScianSliders.h"
#include "ScianIDs.h"
#include "ScianDatasets.h"
#include "ScianErrors.h"
#include "ScianVisObjects.h"
#include "ScianVisMesh.h"
#include "ScianStyle.h"
#include "ScianPictures.h"
#include "ScianTitleBoxes.h"
#include "ScianButtons.h"
#include "ScianMethods.h"
#include "ScianDraw.h"

ObjPtr meshClass;

/*Values for color cells.  Don't change these values.*/
#define COLORCELLSNONE		0
#define COLORCELLSGRID		1
#define COLORCELLSDATA		2

static ObjPtr MeshInit(object)
ObjPtr object;
/*Initializes a mesh object*/
{
    ObjPtr minMax;
    real bounds[6];
    real xySize, zSize;
    ObjPtr colorObj, deformObj;

    MakeVar(object, MESHFIELD);
    SetVar(object, COLOROBJ, colorObj = GetVar(object, MESHFIELD));
    if (colorObj)
    {
	SetVar(object, COLORS, ObjTrue);
    }
    SetVar(object, COLORCELLS, NewInt(1));
    SetVar(object, DEFORMOBJ, deformObj = GetVar(object, MESHFIELD));

    /*Heuristically determine whether to displace Z*/
    GetBounds(object, bounds);
    xySize = bounds[1] - bounds[0];
    xySize = MAX(xySize, bounds[3] - bounds[2]);
    xySize = MAX(xySize, bounds[5] - bounds[4]);
    if (GetVar(colorObj, DATA))
    {
	MakeVar(deformObj, MINMAX);
	minMax = GetVar(deformObj, MINMAX);
	zSize = ((real *) ELEMENTS(minMax))[1] - ((real *) ELEMENTS(minMax))[0];
	if (zSize < xySize * 0.5)
	{
	    SetVar(object, DEFORMSWITCH, NewInt(1));
	}
	else
	{
	    SetVar(object, DEFORMSWITCH, NewInt(0));
	}
    }
}

static void DrawMajorMark(x, yt, yb, selected)
int x, yt, yb;
Bool selected;
/*Draws a major mark at x from yt to yb.  Selected if selected.*/
{
    DrawUILine(x, yt - CCMARKBUTLEN, x, yb, UIBLACK);

    FillUIRect(x - CCMARKBUTWIDTH / 2, x + CCMARKBUTWIDTH / 2,
		yt - CCMARKBUTLEN, yt, UIWHITE);
    if (selected)
    {
	FrameUIRect(x - CCMARKBUTWIDTH / 2, x + CCMARKBUTWIDTH / 2 + 1,
		    yt - CCMARKBUTLEN - 1, yt, UIBLACK);
	FrameUIRect(x - CCMARKBUTWIDTH / 2 - 1, x + CCMARKBUTWIDTH / 2 - 1,
		    yt - CCMARKBUTLEN + 1, yt + 1, UIYELLOW);
	FrameUIRect(x - CCMARKBUTWIDTH / 2 - 2, x + CCMARKBUTWIDTH / 2,
		    yt - CCMARKBUTLEN, yt + 2, UIYELLOW);
    }
    else
    {
	FrameUIRect(x - CCMARKBUTWIDTH / 2, x + CCMARKBUTWIDTH / 2,
		    yt - CCMARKBUTLEN, yt, UIBLACK);
    }
}


static ObjPtr SetMeshMainDataset(visObj, dataSet)
ObjPtr visObj, dataSet;
/*Sets the main data set of visObj to dataSet*/
{
    SetVar(visObj, MESHFIELD, dataSet);
    SetVar(visObj, MAINDATASET, dataSet);
    return ObjTrue;
}

static ObjPtr MakeMeshColored(visObject)
ObjPtr visObject;
/*Makes the mesh colored*/
{
    SetVar(visObject, PICCOLORED, ObjTrue);
    if (GetPredicate(visObject, COLORS))
    {
	ObjPtr meshField, meshForm;
	ObjPtr colorField, colorForm;
	real brightness;
	ObjPtr var;
	ObjPtr surface;
	ObjPtr palette;

	/*Get the mesh field and its form*/
	meshField = GetObjectVar("MakeMeshColored", visObject, MESHFIELD);
	if (!meshField) return ObjFalse;
	meshForm = GetObjectVar("MakeMeshColored", meshField, DATAFORM);
	if (!meshForm) return ObjFalse;

	/*Get the color field and its form*/
	colorField = GetObjectVar("MakeMeshColored", visObject, COLOROBJ);
	if (!colorField) return ObjFalse;
	colorForm = GetObjectVar("MakeMeshColored", colorField, DATAFORM);
	if (!colorForm) return ObjFalse;

	/*Get the color palette*/
	MakeVar(colorField, CPALETTE);
	palette = GetPaletteVar("MakeMeshColored", colorField, CPALETTE);
	if (!palette)
	{
	    return ObjFalse;
	}
	SetPalette(palette);

	/*Get the brightness for coloring*/
	var = GetVar(visObject, BRIGHTNESS);
	if (var)
	{
	    brightness = GetReal(var);
	}
	else
	{
	    brightness = 1.0;
	}

	/*Get the surface to color*/
	surface = GetPictureVar("MakePicColored", visObject, SURFACE);
	if (!surface) return ObjFalse;

	/*See if the mesh form is the same as the color form*/
	if (EqualForm(colorForm, meshForm))
	{
	    /*Yes!  It's easy and fast to traverse*/
	    long info;
	    info = GetDatasetInfo(colorField);

	    if (info & DS_UNSTRUCTURED)
	    {
		/*It's an unstructured data form*/
		ObjPtr faces;			/*Faces of the dataform*/
		PolysPtr polys;			/*The polygons in the surface*/
		PolyPtr polyRunner;		/*The runner through the polys*/
		ThingListPtr cellRunner;	/*The runner through the cells*/
		ObjPtr field;			/*The field itself*/

		SetCurField(FIELD1, meshField);

		faces = GetDatasetKEdges(meshField, 2);
		if (!faces)
		{
		    return ObjFalse;
		}

		/*Look in the polys of the picture*/
		polys = (PolysPtr) ((PicPtr) surface) -> items;
		if (!polys)
		{
		    return ObjTrue;
		}
		if (polys -> item . type != POLYGONS)
		{
		    ReportError("MakeMeshColored", "Malformed picture");
		    return ObjFalse;
		}

		/*Get the data*/
		if (!SetCurField(FIELD1, meshField))
		{
		    return ObjFalse;
		}

		cellRunner = LISTOF(faces);
		polyRunner = polys -> polygons;  
    
		while (cellRunner)
		{
		    ObjPtr array;
		    int k, d, i, index;
		    real *a;

		    if (!polyRunner)
		    {
			ReportError("MakeMeshColored", "Picture too short");
			break;
		    }
		    array = cellRunner -> thing;
		    d = DIMS(array)[0];
		    a = ArrayMeat(array);

		    if (d == polyRunner -> nVertices)
		    {
			for (k = 0; k < d; ++k)
			{
			    /*Color the vertices of the polygon*/
			    long indices[1];
			    i = *a;
			    indices[0] = i;
			    index = GetRealColorIndex(
				SelectFieldScalar(FIELD1, indices));
			    polyRunner -> vertices[k] -> colorIndex = index;
			    ++a;
			}
		    }
		    else
		    {
			ReportError("MakeMeshColored", "Malformed polygon");
		    }

		    cellRunner = cellRunner -> next;
		    polyRunner = (PolyPtr) polyRunner -> item . next;
		}
		if (cellRunner)
		{
		    ReportError("MakeMeshColored", "Picture too long");
		}
	    }
	    else
	    {
		/*Same structured dataset*/
		int topDim;
		long curDim[2], iDims[2];
		ObjPtr dims;
		RectMeshPtr rectMesh;
		real temp[3];
		ObjPtr var;
		int colorCells;
		Bool reverse;

		/*It must have dimension 2*/
		topDim = GetTopDim(meshField);
		if (topDim != 2)
		{
		    ReportError("MakeMeshColored", "Topological dimension must be 2.");
		    return ObjFalse;
		}

		/*Get the actual topological dimensions dimension*/
		dims = GetDatasetFormDims(meshField);
		if (!dims || !IsRealArray(dims) || RANK(dims) != 1 || DIMS(dims)[0] != 2)
		{
		    return ObjFalse;
		}

		iDims[0] = ((real *) ELEMENTS(dims))[0];
		iDims[1] = ((real *) ELEMENTS(dims))[1];

		/*Register the mesh field*/
		if (!SetCurField(FIELD1, meshField))
		{
		    return ObjFalse;
		}

		/*See what kind of color cells it has*/
		var = GetIntVar("MakeMeshColored", visObject, COLORCELLS);
		if (var)
		{
		    colorCells = GetInt(var);
 		}
		else
		{
		    colorCells = 1;
		}
		if (colorCells)
		{
		    real sample;
		    VertexPtr v;

		    int iDim, jDim;

		    reverse = (GetPredicate(visObject, REVERSESENSE) ^
				GetPredicate(meshField, ISLEFTHANDED));

		    if (reverse)
		    {
			iDim = 1;
			jDim = 0;
		    }
		    else
		    {
			iDim = 0;
			jDim = 1;
		    }

		    /*Get the rectangular mesh*/
		    rectMesh = (RectMeshPtr) ((PicPtr) surface) -> items;
		    if (!rectMesh)
		    {
			return ObjFalse;
		    }

		    if (colorCells == COLORCELLSGRID)
		    {
			/*Color cells between the grid lines*/

			for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim])
			{
			    for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim])
			    {
				sample = SelectFieldScalar(FIELD1, curDim);
				v = RectMeshVertex(rectMesh, 
					curDim[iDim], curDim[jDim]);
				v -> colorIndex = GetRealColorIndex(sample);
			    }
			}

			InterpRectCenters(rectMesh);

		    }
		    else if (colorCells == COLORCELLSDATA)
		    {
			/*Color cells around the data points*/

			for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim])
			{
			    for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim])
			    {
				sample = SelectFieldScalar(FIELD1, curDim);
				v = RectMeshCenter(rectMesh, 
					curDim[iDim], curDim[jDim]);
				v -> colorIndex = GetRealColorIndex(sample);
			    }
			}

			InterpRectVertices(rectMesh);
		    }
		}
	    }
	}
	else
	{
	    ColorPictureByObject(surface, colorField, GetPredicate(visObject, INTERPCOLORS));
	}
    }
    return ObjTrue;
}

static ObjPtr MakeMeshSurface(visObject)
ObjPtr visObject;
/*Makes the surface in a mesh object.*/
{
    ObjPtr dataset;		/*The dataset the vis object represents*/
    long datasetFlags;		/*Flags of the dataset*/
    ObjPtr var;			/*Random variable*/
    int colorCells;		/*What kind of color cells*/
    ObjPtr picture;		/*The picture to be made*/
    dataset = GetObjectVar("MakeMeshSurface", visObject, MESHFIELD);
    if (!dataset)
    {
	return ObjFalse;
    }

    datasetFlags = GetDatasetInfo(dataset);

    if (0 == datasetFlags & DS_HASFORM)
    {
	ReportError("MakeMeshSurface", "No data form");
	return ObjFalse;
    }

    /*See what kind of color cells it has*/
    var = GetIntVar("MakeMeshSurface", visObject, COLORCELLS);
    if (var)
    {
	colorCells = GetInt(var);
    }
    else
    {
	colorCells = 1;
    }

    /*Make the new picture*/
    picture = NewPicture();
    if (!picture) return ObjFalse;

    if (datasetFlags & DS_UNSTRUCTURED)
    {
	/*It's an unstructured dataset.*/
	ObjPtr edges;			/*Edges of the dataform*/
	ObjPtr faces;			/*Faces of the dataform*/
	TwoReals *edgesArray;		/*Array of edges*/
	PolysPtr polys;			/*The polygons in the surface*/
	ThingListPtr runner;		/*The runner through the cells*/
	VertexPtr *vertices;		/*Temporary holding place for vertices*/
	long allocVertices;		/*Number of vertices allocated*/
	long nVertices;			/*Total number of vertices*/
	VertexPtr *indVertices;		/*Indirect vertices*/
	long k;				/*Index into form*/
	ObjPtr dims;			/*Dims of the dataset*/

	SetCurForm(FORMFIELD, dataset);

	dims = GetDatasetFormDims(dataset);
	if (!dims)
	{
	    ReportError("MakeMeshSurface", "No form dimensions");
	    return ObjFalse;
	}
	nVertices = *((real *) ELEMENTS(dims));

	edges = GetDatasetKEdges(dataset, 1);
	if (!edges)
	{
	    return ObjFalse;
	}
	edgesArray = (TwoReals *) ArrayMeat(edges);

	faces = GetDatasetKEdges(dataset, 2);
	if (!faces)
	{
	    return ObjFalse;
	}

	if (colorCells)
	{
	    /*Make the picture and set it up with one polys*/
	    polys = AppendPolysToPicture(picture);
	    if (!polys) return ObjFalse;

	    indVertices = (VertexPtr *) malloc(nVertices * sizeof(VertexPtr));
	    for (k = 0; k < nVertices; ++k)
	    {
		indVertices[k] = NewVertex(picture, 0);
		indVertices[k] -> position[0] = SelectFieldComponent(FORMFIELD, 0, &k);
		indVertices[k] -> position[1] = SelectFieldComponent(FORMFIELD, 1, &k);
		indVertices[k] -> position[2] = SelectFieldComponent(FORMFIELD, 2, &k);
	    }

	    allocVertices = 20;
	    vertices = (VertexPtr *) malloc(20 * sizeof(VertexPtr));
	    if (!vertices)
	    {
		return ObjFalse;
	    }
	
	    runner = LISTOF(faces);    
	    while (runner)
	    {
		ObjPtr array;
		int k, d, i;
		real *a;

		array = runner -> thing;
		d = DIMS(array)[0];
		a = ArrayMeat(array);

		for (k = 0; k < d; ++k)
		{
		    i = *a;
		    
		    if (k >= allocVertices)
		    {
			allocVertices += 20;
			vertices = (VertexPtr *) realloc(vertices, allocVertices * sizeof(VertexPtr));
			if (!vertices)
			{
			    return ObjFalse;
			}
		    }
		    vertices[k] = indVertices[i];

		    ++a;
		}
		/*DIKEO fix to do better vertex allocation*/
		AppendSPolyToPolys(polys, d, vertices);

		runner = runner -> next;
	    }
	    free(vertices);
	    free(indVertices);
	}
    }
    else
    {
	/*It's a structured dataset.*/
	int topDim, nComponents;
	long curDim[2], iDims[2];
	ObjPtr dims;
	RectMeshPtr rectMesh;
	real temp[3];
	int iDim, jDim;
	Bool reverse;

	/*It must have dimension 2*/
	topDim = GetTopDim(dataset);
	if (topDim != 2)
	{
	    ReportError("MakeMeshSurface", "Topological dimension must be 2.");
	    return ObjFalse;
	}

	/*Get the actual topological dimensions*/
	dims = GetDatasetFormDims(dataset);
	if (!dims || !IsRealArray(dims) || RANK(dims) != 1 || DIMS(dims)[0] != 2)
	{
	    ReportError("MakeMeshSurface", "No topological dimensions");
	    return ObjFalse;
	}

	iDims[0] = ((real *) ELEMENTS(dims))[0];
	iDims[1] = ((real *) ELEMENTS(dims))[1];

	/*Register the dataset and its dataform*/
	if (!SetCurField(FIELD1, dataset))
	{
	    return ObjFalse;
	}
	if (!SetCurForm(FIELD2, dataset))
	{
	    return ObjFalse;
	}

	reverse = (GetPredicate(visObject, REVERSESENSE) ^
		GetPredicate(dataset, ISLEFTHANDED));

	if (reverse)
	{
	    iDim = 1;
	    jDim = 0;
	}
	else
	{
	    iDim = 0;
	    jDim = 1;
	}

	if (colorCells)
	{
	    /*Create the color cells*/
	    VertexPtr v;

	    if (colorCells == COLORCELLSGRID)
	    {
		/*Color cells between the grid lines*/
	
		/*Create a new rectangular mesh*/
		rectMesh = AppendRectMeshToPicture(picture, iDims[iDim], iDims[jDim], false);
		if (!rectMesh)
		{
		    return ObjFalse;
		}

		nComponents = GetNComponents(FIELD2);

		for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim])
		{
		    for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim])
		    {
			v = RectMeshVertex(rectMesh, curDim[iDim], curDim[jDim]);
			v -> position[0] = SelectFieldComponent(FIELD2, 0, curDim);
			v -> position[1] = SelectFieldComponent(FIELD2, 1, curDim);
			v -> position[2] = nComponents >= 3 ? SelectFieldComponent(FIELD2, 2, curDim) : 0.0;
		    }
		}
		InterpRectCenters(rectMesh);
	    }
	    else if (colorCells == COLORCELLSDATA)
	    {
		/*Color cells around the data points*/

		/*Create a new rectangular mesh*/
		rectMesh = AppendRectMeshToPicture(picture, iDims[iDim] + 1, iDims[jDim] + 1, true);
		if (!rectMesh)
		{
		    return ObjFalse;
		}

		nComponents = GetNComponents(FIELD2);

		for (curDim[iDim] = 0; curDim[iDim] < iDims[iDim]; ++curDim[iDim])
		{
		    for (curDim[jDim] = 0; curDim[jDim] < iDims[jDim]; ++curDim[jDim])
		    {
			v = RectMeshCenter(rectMesh, curDim[iDim], curDim[jDim]);
			v -> position[0] = SelectFieldComponent(FIELD2, 0, curDim);
			v -> position[1] = SelectFieldComponent(FIELD2, 1, curDim);
			v -> position[2] = nComponents >= 3 ? SelectFieldComponent(FIELD2, 2, curDim) : 0.0;
		    }
		}
		InterpRectVertices(rectMesh);
	    }
	}
    }

    CalcPictureNormals(picture);
    SetVar(visObject, SURFACE, picture);
    SetVar(picture, REPOBJ, visObject);
    return ObjTrue;
}

static ObjPtr ChangeMeshColorCells(button)
ObjPtr button;
/*Changes the color cells as a result of a change in the value of button*/
{
    ObjPtr repObj;
    ObjPtr value;

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

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

static ObjPtr AddMeshControls(mesh, panelContents)
ObjPtr mesh, panelContents;
/*Adds controls appropriate to a mesh object to panelContents*/
{
    ObjPtr titleBox, button, radio, var, corral, icon, name, meshField, mainDataset;
    ObjPtr textBox, defaultIcon;
    int width, left, top, bottom, right;
    ObjPtr control;
    ObjPtr minMax;
    real min, max, *elements;

    width = CWINWIDTH - 2 * CORRALBORDER - CWINCORRALWIDTH;

    /*Get the mesh field*/
    meshField = GetObjectVar("AddMeshControls", mesh, MESHFIELD);
    if (!meshField) return ObjFalse;
    while (mainDataset = GetVar(meshField, MAINDATASET))
    {
	meshField = mainDataset;
    }

    /*Find its min and max*/
    MakeVar(meshField, MINMAX);
    minMax = GetVar(meshField, MINMAX);
    if (minMax)
    {
	elements = ELEMENTS(minMax);
	min = elements[0];
	max = elements[1];
    }
    else
    {
	min = 0.0;
	max = 1.0;
    }

    /*Create the color cells title box*/
    right = width;
    left = right - (CCBUTTONLENGTH + 2 * MINORBORDER);
    top = CWINHEIGHT - MINORBORDER;
    bottom = top - (2 * MINORBORDER + 3 * CHECKBOXHEIGHT + 2 * CHECKBOXSPACING + TITLEBOXTOP);

    titleBox = NewTitleBox(left, right, bottom, top, "Color Cells");
    PrefixList(panelContents, titleBox);
    SetVar(titleBox, PARENT, panelContents);

    /*Create the radio button group*/
    radio = NewRadioButtonGroup("Color Cells Radio");
    PrefixList(panelContents, radio);
    SetVar(radio, PARENT, panelContents);
    SetVar(radio, REPOBJ, mesh);

    /*Create the buttons*/
    left += MINORBORDER;
    right -= MINORBORDER;
    top -= TITLEBOXTOP + MINORBORDER;
    bottom = top - CHECKBOXHEIGHT;
    button = NewRadioButton(left, right, bottom, top, "None");
    AddRadioButton(radio, button);

    top = bottom - CHECKBOXSPACING;
    bottom = top - CHECKBOXHEIGHT;
    button = NewRadioButton(left, right, bottom, top, "Between Grid Lines");
    AddRadioButton(radio, button);

    top = bottom - CHECKBOXSPACING;
    bottom = top - CHECKBOXHEIGHT;
    button = NewRadioButton(left, right, bottom, top, "Around Data Points");
    AddRadioButton(radio, button);

    /*Set the radio according to COLORCELLS*/
    var = GetIntVar("AddMeshControls", mesh, COLORCELLS);
    if (var)
    {
	SetValue(radio, var);
    }

    SetMethod(radio, CHANGEDVALUE, ChangeMeshColorCells);

    /*Put in the mesh corral at the top left*/
    left = MAJORBORDER;
    top = MAJORBORDER;
    corral = NewIconCorral(NULLOBJ,
			   left, left + ONECORRALWIDTH,
			   CWINHEIGHT - MAJORBORDER - ONECORRALHEIGHT,
			   CWINHEIGHT - MAJORBORDER, 0);
    SetVar(corral, SINGLECORRAL, ObjTrue);
    SetVar(corral, TOPDOWN, ObjTrue);
    SetVar(corral, NAME, NewString("Mesh Field"));
    SetVar(corral, HELPSTRING,
	NewString("This corral shows the dataset that is being used to make \
the mesh display.  The locations of color cells and meshs are calculated \
using this field.  The color of color cells is calculated using the Color Field, \
available in the Color control set."));
    PrefixList(panelContents, corral);
    SetVar(corral, PARENT, panelContents);
    SetVar(corral, REPOBJ, mesh);
    SetMethod(corral, DROPINCONTENTS, DropInMainDatasetCorral);

    /*Create the mesh source text box*/
    textBox = NewTextBox(left, left + ONECORRALWIDTH, 
			 CWINHEIGHT - MAJORBORDER - ONECORRALHEIGHT - TEXTBOXSEP - TEXTBOXHEIGHT,
			 CWINHEIGHT - MAJORBORDER - ONECORRALHEIGHT - TEXTBOXSEP,
			 0, "Mesh Field Text", "Mesh Field");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    /*Put in an icon that represents the field*/
    name = GetVar(meshField, NAME);
    defaultIcon = GetVar(meshField, DEFAULTICON);
    if (defaultIcon)
    {
	icon = NewObject(defaultIcon, 0);
	SetVar(icon, NAME, name);
    }
    else
    {
	icon = NewIcon(0, 0, ICONQUESTION, GetString(name));
    }
    SetVar(icon, ICONLOC, NULLOBJ);
    SetVar(icon, REPOBJ, meshField);
    DropIconInCorral(corral, icon);

    return ObjTrue;
}

void InitMeshes()
/*Initializes the mesh object*/
{
    ObjPtr icon, color;

    /*Class for a mesh object*/
    meshClass = NewObject(visDeformed, 0);
    AddToReferenceList(meshClass);
    SetVar(meshClass, NAME, NewString("Mesh"));
    SetMethod(meshClass, INITIALIZE, MeshInit);
    SetVar(meshClass, SHINVAL, NewReal(80.0));
    SetVar(meshClass, SPECVAL, NewReal(0.2));
    SetVar(meshClass, DEFAULTICON, icon = NewObject(visIcon, 0));
    SetVar(icon, WHICHICON, NewInt(ICONMESH));
    SetVar(icon, NAME, NewString("Mesh"));
    SetVar(icon, HELPSTRING,
	NewString("This icon represents a mesh visualization object.  \
The mesh object shows shaded mesh surfaces of 2-dimensional \
scalar fields defined over structured or nonstructured grids."));
    DeclareIndirectDependency(meshClass, SURFACE, MESHFIELD, DATA);
    DeclareIndirectDependency(meshClass, SURFACE, MESHFIELD, CURDATA);
    DeclareDependency(meshClass, SURFACE, COLORCELLS);
    SetMethod(meshClass, SURFACE, MakeMeshSurface);
    DeclareDependency(meshClass, SURFACE, REVERSESENSE);
    SetMethod(meshClass, PICCOLORED, MakeMeshColored);
    SetMethod(meshClass, SETMAINDATASET, SetMeshMainDataset);

    SetMethod(meshClass, ADDCONTROLS, AddMeshControls);
    icon = NewIcon(0, 0, ICONMESH, "Mesh");
    SetVar(meshClass, CONTROLICON, icon);

    DefineVisMapping(DS_HASFORM | DS_HASFIELD, 2, 3, 1, meshClass);
    DefineVisMapping(DS_HASFORM | DS_HASFIELD, 2, 2, 1, meshClass);
    DefineVisMapping(DS_HASFORM | DS_HASFIELD | DS_UNSTRUCTURED, 2, 3, 1, meshClass);
    DefineVisMapping(DS_HASFORM | DS_HASFIELD | DS_UNSTRUCTURED, 2, 2, 1, meshClass);
}

void KillMeshes()
/*Kills the mesh*/
{
    DeleteThing(meshClass);
}
