/*ScianGeometry.c
  Eric Pepke
  8 Feb 1993
  New geometry stuff within SciAn
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianNames.h"
#include "ScianGeometry.h"
#include "ScianIDs.h"
#include "ScianArrays.h"
#include "ScianLists.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianPictures.h"
#include "ScianDatasets.h"

ObjPtr geoObjClass;			/*Class of all geometry objects*/

ObjPtr NewGeometry()
/*Returns a new geometry object*/
{
    ObjPtr retVal;

    retVal = NewObject(geoObjClass, sizeof(Geometry) - sizeof(Thing));
    retVal -> flags = OT_GEOMETRY;
    ((GeoPtr) retVal) -> items = 0;
    ((GeoPtr) retVal) -> nAlloc = 0;
    ((GeoPtr) retVal) -> nWritten = 0;
}

ObjPtr CleanupGeometry(geo)
ObjPtr geo;
/*Cleans up a geometry object*/
{
    if (((GeoPtr) geo) -> items)
    {
	free(((GeoPtr) geo) -> items);
	((GeoPtr) geo) -> items = NULL;
    }
    return ObjTrue;
}

#ifdef PROTO
static AppendLongToGeometry(ObjPtr geometry, long item)
#else
static AppendLongToGeometry(geometry, item)
#endif
{
    if (((GeoPtr) geometry) -> nWritten >=
	((GeoPtr) geometry) -> nAlloc)
    {
	/*Have to reallocate some stuff*/
	if (((GeoPtr) geometry) -> nAlloc)
	{
	    ((GeoPtr) geometry) -> nAlloc += GEOCHUNK;
	    ((GeoPtr) geometry) -> items = realloc(((GeoPtr) geometry) -> items, ((GeoPtr) geometry) -> nAlloc * sizeof(long));
	}
	else
	{
	    ((GeoPtr) geometry) -> nAlloc += GEOCHUNK;
	    ((GeoPtr) geometry) -> items = malloc(((GeoPtr) geometry) -> nAlloc * sizeof(long));
	}
    }
    ((GeoPtr) geometry) -> items[((GeoPtr) geometry) -> nWritten] = item;
    ++(((GeoPtr) geometry) -> nWritten);
}

#ifdef PROTO
void AppendPolygonToGeometry(ObjPtr geometry, long nVertices, long vertices[])
#else
void AppendPolygonToGeometry(geometry, nVertices, vertices)
ObjPtr geometry;
long nVertices;
long vertices[];
#endif
/*Appends a polygon to a geometry frame*/
{
    long k;
    AppendLongToGeometry(geometry, IT_POLYGON);
    AppendLongToGeometry(geometry, nVertices);
    for (k = 0; k < nVertices; ++k)
    {
	AppendLongToGeometry(geometry, vertices[k]);
    }
}

#ifdef PROTO
ObjPtr ConvertDatasetToPicture(ObjPtr dataset, ObjPtr normalsObj)
#else
ObjPtr ConvertDatasetToPicture(dataset, normalsObj)
ObjPtr dataset;
ObjPtr normalsObj;
#endif
/*Converts a dataset to a picture.  normalsObj may provide normals*/
{
    GeoPtr geometry;
    long k;
    long dim;
    long curItemWord;
    long nVertices;
    ObjPtr retVal;
    VertexPtr *vertices;
    long nTempVertices;
    VertexPtr *tempVertices;
    PolysPtr polys;
    real position[3], normal[3];
    Bool equalNormalForm;

    SetCurForm(FORMFIELD, dataset);

    MakeVar(dataset, CURDATA);
    geometry = (GeoPtr) GetVar(dataset, CURDATA);
    if (!geometry || !IsGeometry(geometry))
    {
	ReportError("ConvertDatasetToPicture", "No valid geometry");
	return NULLOBJ;
    }

    if (CountTraversalDims(FORMFIELD) != 1)
    {
	ReportError("ConvertDatasetToPicture", "Bad data form");
	return NULLOBJ;
    }

    GetTraversalDims(FORMFIELD, &dim);

    /*See if normals are set and the same*/
    equalNormalForm = false;
    if (normalsObj)
    {
	SetCurField(FIELD1, normalsObj);
	if (IdenticalFields(FORMFIELD, FIELD1))
	{
	    equalNormalForm = true;
	}
    }

    /*Create the picture*/
    retVal = NewPicture();
    if (!retVal) return NULLOBJ;

    polys = AppendPolysToPicture(retVal);

    /*Make temporary holder for vertices*/
    vertices = (VertexPtr *) malloc(dim * sizeof(VertexPtr));
    if (!vertices) 
    {
	OMErr();
	return NULLOBJ;
    }

    /*Go through and make the vertices*/
    for (k = 0; k < dim; ++k)
    {
	vertices[k] = NewVertex(retVal, k ? VF_NEXTCANON : VF_FIRSTCANON);
	vertices[k] -> position[0] = SelectFieldComponent(FORMFIELD, 0, &k);
	vertices[k] -> position[1] = SelectFieldComponent(FORMFIELD, 1, &k);
 	vertices[k] -> position[2] = SelectFieldComponent(FORMFIELD, 2, &k);
	if (normalsObj)
	{
	    if (equalNormalForm)
	    {
		vertices[k] -> normal[0] = SelectFieldComponent(FIELD1, 0, &k);
		vertices[k] -> normal[1] = SelectFieldComponent(FIELD1, 1, &k);
	 	vertices[k] -> normal[2] = SelectFieldComponent(FIELD1, 2, &k);
	    }
	    else
	    {
		position[0] = vertices[k] -> position[0];
		position[1] = vertices[k] -> position[1];
		position[2] = vertices[k] -> position[2];
		SampleSpatComponent(FIELD1, FORMFIELD, 3,
		    normal, 3, position, true);
		vertices[k] -> normal[0] = normal[0];
		vertices[k] -> normal[1] = normal[1];
		vertices[k] -> normal[2] = normal[2];
	    }
	}
    }

    /*Make temp vertices for producing polygons and stuff*/
    tempVertices = (VertexPtr *) malloc(20 * sizeof(VertexPtr));
    nTempVertices = 20;

    /*Now parse the geometry*/
    curItemWord = 0;
    while (curItemWord < geometry -> nWritten)
    {
	switch(geometry -> items[curItemWord])
	{
	    case IT_POLYGON:
		++curItemWord;
		nVertices = geometry -> items[curItemWord];
		++curItemWord;
		if (nVertices > nTempVertices)
		{
		    nTempVertices = nVertices;
		    tempVertices = (VertexPtr *) realloc(tempVertices, nTempVertices * sizeof(VertexPtr));
		}
		for (k = 0; k < nVertices; ++k)
		{
		    tempVertices[k] = vertices[geometry -> items[curItemWord]];
		    ++curItemWord;
		}
		if (nVertices)
		{
		    /*Append a polygon*/
		    AppendSPolyToPolys(polys, nVertices, tempVertices);
		}
		break;
	    default:
		ReportError("ConvertDatasetToPicture", "Bad geometry item");
		curItemWord = geometry -> nWritten;
		break;
	}
    }

    /*Get rid of temporary vertex arrays*/
    SAFEFREE(tempVertices);
    SAFEFREE(vertices);

    if (!normalsObj)
    {
	CalcPictureNormals(retVal);
    }

    return retVal;
}

void InitGeometry()
{
    geoObjClass = NewObject(NULLOBJ, sizeof(Geometry) - sizeof(Thing));
    geoObjClass -> flags = OT_GEOMETRY;
    ((GeoPtr) geoObjClass) -> items = 0;
    ((GeoPtr) geoObjClass) -> nAlloc = 0;
    ((GeoPtr) geoObjClass) -> nWritten = 0;
    SetMethod(geoObjClass, CLEANUP, CleanupGeometry);
    AddToReferenceList(geoObjClass);
}

void KillGeometry()
{
    DeleteThing(geoObjClass);
}
