/*ScianVisPoints.c
  Sept 23, 
  Eric Pepke
  Point cloud visualization object 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 "ScianSliders.h"
#include "ScianIDs.h"
#include "ScianDatasets.h"
#include "ScianPictures.h"
#include "ScianDialogs.h"
#include "ScianErrors.h"
#include "ScianComplexControls.h"
#include "ScianMethods.h"
#include "ScianStyle.h"
#include "ScianVisObjects.h"
#include "ScianVisPoints.h"
#include "ScianDraw.h"
#include "ScianTemplates.h"
#include "ScianTemplateHelper.h"

ObjPtr visPoints;	/*Class for point cloud*/

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

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

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

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

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

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

    SetVar(repObj, POINTSTYLE, NewInt(flags));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeShowMissing(object)
ObjPtr object;
/*Changed value for SHOWMISSING*/
{
    ObjPtr repObj;
 
    repObj = GetObjectVar("ChangeShowMissing", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, SHOWMISSING, GetValue(object));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeRandomJitter(object)
ObjPtr object;
/*Changed value for RANDOMJITTER*/
{
    ObjPtr repObj;
 
    repObj = GetObjectVar("ChangeRandomJitter", object, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, RANDOMJITTER, GetValue(object));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr AddPointsControls(object, panelContents)
ObjPtr object, panelContents;
/*Adds controls to a points object*/
{
    ObjPtr titleBox, checkBox;
    ObjPtr textBox, corral;
    ObjPtr icon, name, defaultIcon;
    ObjPtr pointsField, mainDataset;
    ObjPtr var;
    int left, right, bottom, top;
    int l, r, b, t;

    /*Get the field*/
    pointsField = GetObjectVar("AddPointsControls", object, POINTSFIELD);
    if (!pointsField) return ObjFalse;
    while (mainDataset = GetVar(pointsField, MAINDATASET))
    {
	pointsField = mainDataset;
    }


    /*Put in the source 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("Points Field"));
    SetVar(corral, HELPSTRING,
	NewString("This corral shows the dataset that is being used to make \
the point cloud display."));
    PrefixList(panelContents, corral);
    SetVar(corral, PARENT, panelContents);
    SetVar(corral, REPOBJ, object);
    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, "Points Field Text", "Points Field");
    PrefixList(panelContents, textBox);
    SetVar(textBox, PARENT, panelContents);
    SetTextAlign(textBox, CENTERALIGN);

    /*Put in an icon that represents the field*/
    name = GetVar(pointsField, NAME);
    defaultIcon = GetVar(pointsField, 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, pointsField);
    DropIconInCorral(corral, icon);

    /*Make check boxes*/
    checkBox = TemplateCheckBox(VisPointCloudTemplate, "Show Missing Data", GetPredicate(object, SHOWMISSING));
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    SetMethod(checkBox, CHANGEDVALUE, ChangeShowMissing);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, all data points will be shown, \
even if the data point is missing.  When it is not checked, missing data points \
will not be shown."));

    checkBox = TemplateCheckBox(VisPointCloudTemplate, "Random Jitter", GetPredicate(object, RANDOMJITTER));
    PrefixList(panelContents, checkBox);
    SetVar(checkBox, PARENT, panelContents);
    SetVar(checkBox, REPOBJ, object);
    SetMethod(checkBox, CHANGEDVALUE, ChangeRandomJitter);
    SetVar(checkBox, HELPSTRING,
	NewString("When this box is checked, all data points will be randomly jittered.  \
This can help reduce the regularity of the grid points and give a smoother \
appearance.  Jittered dots take much more time to generate but are displayed \
at the same speed."));

    return ObjTrue;
}

typedef VertexPtr SixVertices[6];
typedef VertexPtr CubeVertices[2][2][2];

static ObjPtr MakePointsSurface(points)
ObjPtr points;
/*Makes the surface of a points object*/
{
    ObjPtr pointsData;
    ObjPtr surface;
    ObjPtr var;

    surface = NewPicture();

    /*Get the main dataset*/
    MakeVar(points, POINTSFIELD);
    pointsData = GetVar(points, POINTSFIELD);

    if (pointsData)
    {
	/*There is some data.  Put stuff on the surface*/
	int nTraversalDims;		/*Number of dimensions to traverse*/
	int whichDim;			/*Dimension counter*/
	long *traversalDims = 0;	/*Dimensions of the dataset to traverse*/
	long *index = 0;		/*Counting index*/
	long *jitterIndex = 0;		/*Index for jitter*/
	long nNodes;			/*# Total nodes in the dataset*/
	VertexPtr *centerVertices = 0;	/*Center vertices*/
	long nVertices;			/*Number of vertices so far*/
	int nComponents;		/*# components of data form*/
	VertexPtr tv;			/*Temporary vertex*/
	long k;				/*Counter*/
	real x, y, z, d;
	real x1, y1, z1;		/*Points for jittering*/
	real x2, y2, z2;		/*Points for jittering*/
	real w;				/*Jittering weight*/
	PicItemPtr item;
	Bool sameForm;			/*True iff forms are the same*/
	Bool showMissing;		/*True iff show missing data*/
	Bool randomJitter;		/*Random jitter*/
	Bool isMissing;			/*True iff this point is missing*/

	showMissing = GetPredicate(points, SHOWMISSING);
	randomJitter = GetPredicate(points, RANDOMJITTER);

	SetCurField(FIELD1, pointsData);
	SetCurForm(FORMFIELD, pointsData);
	nComponents = GetNComponents(FORMFIELD);

	/*Get the information on traversing the dataset*/
	nTraversalDims = CountTraversalDims(FORMFIELD);
	if (nTraversalDims)
	{
	    traversalDims = (long *) malloc(sizeof(long) * nTraversalDims);
	    index = (long *) malloc(sizeof(long) * nTraversalDims);
	    if (randomJitter)
	    {
		jitterIndex = (long *) malloc(sizeof(long) * nTraversalDims);
		srandom(37);
	    }
	}
	else
	{
	    index = (long *) malloc(sizeof(long));
	}
	GetTraversalDims(FORMFIELD, traversalDims);

	/*Calculate the number of nodes*/
	nNodes = 1;
	for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
	{
	    if (traversalDims[whichDim] > 0)
	    {
		nNodes *= traversalDims[whichDim];
	    }
	}

	/*Make as many vertices as we might need nodes*/
	centerVertices = (VertexPtr *) malloc(sizeof(VertexPtr) * nNodes);
	if (!centerVertices)
	{
	    OMErr();
	    return ObjFalse;
	}

	/*Fill the vertices*/

	nVertices = 0;

	/*Zero the index*/
	for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
	{
	    index[whichDim] = 0;
	}

	/*Traverse all the points*/
	do
	{
	    isMissing = false;

	    /*See if we need to show the point*/
	    if (showMissing)
	    {
	    }
	    else
	    {
		/*Hide a missing point*/
		d = SelectFieldScalar(FIELD1, index);
		if (d == missingData)
		{
		    isMissing = true;
		}
	    }

	    if (isMissing)
	    {
		/*It's missing, who cares what point it is?*/
		x = 0.0;
		y = 0.0;
		z = 0.0;
	    }
	    else
	    {
		/*Sample the location at a point*/
		x = (0 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 0, index);
		y = (1 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 1, index);
		z = (2 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 2, index);
		if (x == missingData)
		{
		    isMissing = true;
		    x = 0.0;
		}
		if (y == missingData)
		{
		    isMissing = true;
		    y = 0.0;
		}
		if (z == missingData)
		{
		    isMissing = true;
		    z = 0.0;
		}

		if (randomJitter)
		{
		    /*Jitter the point.  First find lower corner*/
		    for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
		    {
			if (traversalDims[whichDim] > 0)
			{
			    if (index[whichDim] <= 0)
			    {
				jitterIndex[whichDim] = 0;
			    }
			    else 
			    {
				jitterIndex[whichDim] = index[whichDim] - 1;
			    }
			}
		    }
		    x1 = (0 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 0, jitterIndex);
		    y1 = (1 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 1, jitterIndex);
		    z1 = (2 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 2, jitterIndex);
		    if (x1 == missingData || y1 == missingData || z1 == missingData)
		    {
			x1 = x; y1 = y; z1 = z;
		    }
		    x1 = (x1 + x) * 0.5;
		    y1 = (y1 + y) * 0.5;
		    z1 = (z1 + z) * 0.5;

		    /*Now the upper corner*/
		    for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
		    {
			if (traversalDims[whichDim] > 0)
			{
			    if (index[whichDim] >= traversalDims[whichDim])
			    {
				jitterIndex[whichDim] = traversalDims[whichDim];
			    }
			    else 
			    {
				jitterIndex[whichDim] = index[whichDim] + 1;
			    }
			}
		    }
		    x2 = (0 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 0, jitterIndex);
		    y2 = (1 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 1, jitterIndex);
		    z2 = (2 >= nComponents) ? 0.0 : SelectFieldComponent(FORMFIELD, 2, jitterIndex);
		    if (x2 == missingData || y2 == missingData || z2 == missingData)
		    {
			x2 = x; y2 = y; z2 = z;
		    }
		    x2 = (x2 + x) * 0.5;
		    y2 = (y2 + y) * 0.5;
		    z2 = (z2 + z) * 0.5;

		    /*Now do a weighted average to get the new point*/
		    w = (random() % 255) / 255.0;
		    x = x1 * w + x2 * (1.0 - w);
		    w = (random() % 255) / 255.0;
		    y = y1 * w + y2 * (1.0 - w);
		    w = (random() % 255) / 255.0;
		    z = z1 * w + z2 * (1.0 - w);
		}
	    }

	    /*Make a vertex*/
	    if (nVertices)
	    {
		centerVertices[nVertices] = NewVertex(surface, VF_NEXTCANON | (isMissing ? VF_DONTDRAW : 0));
	    }
	    else
	    {
		centerVertices[nVertices] = NewVertex(surface, VF_FIRSTCANON | (isMissing ? VF_DONTDRAW : 0));		
	    }

	    /*Make some center vertices*/
	    centerVertices[nVertices] -> position[0] = x;
	    centerVertices[nVertices] -> position[1] = y;
	    centerVertices[nVertices] -> position[2] = z;
	    centerVertices[nVertices] -> normal[0] = 0.0;
	    centerVertices[nVertices] -> normal[0] = 0.0;
	    centerVertices[nVertices] -> normal[0] = 1.0;
	    centerVertices[nVertices] -> colorIndex = 2;

	    ++nVertices;

	    /*Advance to next point*/
	    for (whichDim = 0; whichDim < nTraversalDims; ++whichDim)
	    {
		if (traversalDims[whichDim] > 0)
		{
		    if ((++index[whichDim]) >= traversalDims[whichDim])
		    {
			index[whichDim] = 0;
		    }
		    else
		    {
			break;
		    }
		}
	    }
	} while (whichDim < nTraversalDims); /*Break is based on advance*/
	
	/*Make some points*/
	AppendSPolypointToPicture(surface, 1, nVertices, centerVertices);


	SetVar(surface, MAINDATASET, pointsData);

	/*Free up temporary storage*/
	SAFEFREE(traversalDims);
	SAFEFREE(index);
	if (randomJitter)
	{
	    SAFEFREE(jitterIndex);
	}
	SAFEFREE(centerVertices);
    }

    SetVar(points, SURFACE, surface);
    SetVar(surface, REPOBJ, points);
    return ObjTrue;
}

static ObjPtr PointsInit(points)
ObjPtr points;
/*Initializes a points object*/
{
    ObjPtr colorObj;
    ObjPtr pointsField;
 
    MakeVar(points, POINTSFIELD);
    SetVar(points, COLOROBJ, colorObj = GetVar(points, POINTSFIELD));
    if (colorObj)
    {
	SetVar(points, COLORS, ObjTrue);
    }

    return ObjTrue;
}

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

void InitPoints()
/*Initializes points visualization object*/
{
    ObjPtr icon;

    /*Class for a point cloud*/
    visPoints = NewObject(visDots, 0);
    AddToReferenceList(visPoints);
    SetVar(visPoints, NAME, NewString("Point Cloud"));
    SetMethod(visPoints, INITIALIZE, PointsInit);
    SetVar(visPoints, DEFAULTICON, icon = NewObject(visIcon, 0));
    SetVar(icon, WHICHICON, NewInt(ICONPOINTS));
    SetVar(icon, NAME, NewString("Point Cloud"));
    SetVar(icon, HELPSTRING,
	NewString("This icon represents a point cloud or ball display object.  \
This object can show a point cloud or group of balls for just about any data field."));
    DeclareIndirectDependency(visPoints, SURFACE, MAINDATASET, DATA);
    DeclareIndirectDependency(visPoints, SURFACE, MAINDATASET, CURDATA);
    DeclareIndirectDependency(visPoints, SURFACE, MAINDATASET, CURDATAFORM);
    DeclareIndirectDependency(visPoints, SURFACE, MAINDATASET);
    SetVar(visPoints, SHOWMISSING, ObjFalse);
    SetVar(visPoints, RANDOMJITTER, ObjFalse);
    DeclareDependency(visPoints, SURFACE, SHOWMISSING);
    DeclareDependency(visPoints, SURFACE, RANDOMJITTER);
    SetVar(visPoints, DRAWDOTS, ObjTrue);
    SetMethod(visPoints, SURFACE, MakePointsSurface);
    DeclareDependency(visPoints, SURFACE, POINTSTYLE);
    SetMethod(visPoints, SETMAINDATASET, SetPointsMainDataset);
    SetVar(visPoints, STYLE, NewInt(0));

    SetMethod(visPoints, ADDCONTROLS, AddPointsControls);
    icon = NewIcon(0, 0, ICONPOINTS, "Point Cloud");
    SetVar(icon, HELPSTRING,
	NewString("Click on this icon to see a panel of controls for the point object."));
    SetVar(visPoints, CONTROLICON, icon);

    DefineVisMapping(DS_HASFORM | DS_HASFIELD | DS_VECTOR, -1, -1, -1, visPoints);
    DefineVisMapping(DS_HASFORM | DS_HASFIELD, -1, -1, -1, visPoints);
    DefineVisMapping(DS_HASFORM | DS_HASFIELD | DS_VECTOR | DS_UNSTRUCTURED, -1, -1, -1, visPoints);
    DefineVisMapping(DS_HASFORM | DS_HASFIELD | DS_UNSTRUCTURED, -1, -1, -1, visPoints);
}

void KillPoints()
/*Kills points visualization*/
{
    DeleteThing(visPoints);
}
