#include "GEO_PrimTetra.h"
#include <UT/UT_Defines.h>
#include <UT/UT_Math.h>
#include <UT/UT_SparseArray.h>
#include <UT/UT_SysClone.h>
#include <UT/UT_IStream.h>
#include <GA/GA_AttributeRefMap.h>
#include <GA/GA_AttributeRefMapDestHandle.h>
#include <GA/GA_Defragment.h>
#include <GA/GA_WorkVertexBuffer.h>
#include <GA/GA_WeightedSum.h>
#include <GA/GA_MergeMap.h>
#include <GA/GA_IntrinsicDef.h>
#include <GA/GA_IntrinsicEval.h>
#include <GA/GA_ElementWrangler.h>
#include <GA/GA_PrimitiveJSON.h>
#include <GA/GA_SaveMap.h>
#include <GEO/GEO_Detail.h>
#include <GEO/GEO_PrimType.h>
#include <GEO/GEO_AttributeHandleList.h>
#include <GEO/GEO_WorkVertexBuffer.h>
using namespace HDK_Sample;
#define PT_PER_TET 4
GEO_PrimTetra::GEO_PrimTetra(GEO_Detail *d, GA_Offset offset)
: GEO_Primitive(d, offset)
{
for (int i = 0; i < PT_PER_TET; i++)
{
myVertexOffsets[i] = allocateVertex();
}
}
GEO_PrimTetra::~GEO_PrimTetra()
{
for (int i = 0; i < PT_PER_TET; i++)
{
if (fastVertexOffset(i) != GA_INVALID_OFFSET)
destroyVertex(fastVertexOffset(i));
}
}
void
GEO_PrimTetra::clearForDeletion()
{
for (int i = 0; i < PT_PER_TET; i++)
myVertexOffsets[i] = GA_INVALID_OFFSET;
GEO_Primitive::clearForDeletion();
}
void
GEO_PrimTetra::stashed(int onoff, GA_Offset offset)
{
GEO_Primitive::stashed(onoff, offset);
for (int i = 0; i < PT_PER_TET; i++)
myVertexOffsets[i] = onoff ? GA_INVALID_OFFSET : allocateVertex();
}
bool
GEO_PrimTetra::evaluatePointRefMap(GA_Offset result_vtx,
GA_AttributeRefMap &map,
fpreal u, fpreal v, unsigned du,
unsigned dv) const
{
map.copyValue(GA_ATTRIB_VERTEX, result_vtx,
GA_ATTRIB_VERTEX, getVertexOffset(0));
return true;
}
void
GEO_PrimTetra::reverse()
{
int r, nr;
for (r = 0; r < 2; r++)
{
nr = 3 - r;
GA_Offset other = fastVertexOffset(nr);
myVertexOffsets[nr] = fastVertexOffset(r);
myVertexOffsets[r] = other;
}
}
UT_Vector3
GEO_PrimTetra::computeNormal() const
{
return UT_Vector3(0, 0, 0);
}
fpreal
GEO_PrimTetra::calcVolume(const UT_Vector3 &) const
{
UT_Vector3 v0, v1, v2, v3;
v0 = getParent()->getPos3(vertexPoint(0));
v1 = getParent()->getPos3(vertexPoint(1));
v2 = getParent()->getPos3(vertexPoint(2));
v3 = getParent()->getPos3(vertexPoint(3));
float signedvol = -(v3-v0).dot(cross(v1-v0, v2-v0));
signedvol /= 6;
return signedvol;
}
fpreal
GEO_PrimTetra::calcArea() const
{
float area;
area = 0;
UT_Vector3 v0, v1, v2, v3;
v0 = getParent()->getPos3(vertexPoint(0));
v1 = getParent()->getPos3(vertexPoint(1));
v2 = getParent()->getPos3(vertexPoint(2));
v3 = getParent()->getPos3(vertexPoint(3));
area += cross((v1 - v0), (v2 - v0)).length();
area += cross((v3 - v1), (v2 - v1)).length();
area += cross((v3 - v0), (v1 - v0)).length();
area += cross((v2 - v0), (v3 - v0)).length();
area = area / 2;
return area;
}
fpreal
GEO_PrimTetra::calcPerimeter() const
{
float length = 0;
UT_Vector3 v0, v1, v2, v3;
v0 = getParent()->getPos3(vertexPoint(0));
v1 = getParent()->getPos3(vertexPoint(1));
v2 = getParent()->getPos3(vertexPoint(2));
v3 = getParent()->getPos3(vertexPoint(3));
length += (v1-v0).length();
length += (v2-v0).length();
length += (v3-v0).length();
length += (v2-v1).length();
length += (v3-v1).length();
length += (v3-v2).length();
return 0.0f;
}
GA_Size
GEO_PrimTetra::getVertexCount(void) const
{
return PT_PER_TET;
}
int
GEO_PrimTetra::detachPoints(GA_PointGroup &grp)
{
int i;
int count = 0;
for (i = 3; i >= 0; i--)
if (grp.containsOffset(vertexPoint(i)))
count++;
if (count == 0)
return 0;
if (count == PT_PER_TET)
return -2;
return -1;
}
GA_Primitive::GA_DereferenceStatus
GEO_PrimTetra::dereferencePoint(GA_Offset, bool)
{
if (isDegenerate())
return GA_DEREFERENCE_DEGENERATE;
return GA_DEREFERENCE_FAIL;
}
GA_Primitive::GA_DereferenceStatus
GEO_PrimTetra::dereferencePoints(const GA_RangeMemberQuery &, bool)
{
if (isDegenerate())
return GA_DEREFERENCE_DEGENERATE;
return GA_DEREFERENCE_FAIL;
}
namespace HDK_Sample {
class geo_PrimTetraJSON : public GA_PrimitiveJSON
{
public:
geo_PrimTetraJSON()
{
}
virtual ~geo_PrimTetraJSON() {}
enum
{
geo_TBJ_VERTEX,
geo_TBJ_ENTRIES
};
const GEO_PrimTetra *tet(const GA_Primitive *p) const
{ return static_cast<const GEO_PrimTetra *>(p); }
GEO_PrimTetra *tet(GA_Primitive *p) const
{ return static_cast<GEO_PrimTetra *>(p); }
virtual int getEntries() const { return geo_TBJ_ENTRIES; }
virtual const char *getKeyword(int i) const
{
switch (i)
{
case geo_TBJ_VERTEX: return "vertex";
case geo_TBJ_ENTRIES: break;
}
UT_ASSERT(0);
return NULL;
}
virtual bool saveField(const GA_Primitive *pr, int i,
UT_JSONWriter &w, const GA_SaveMap &map) const
{
switch (i)
{
case geo_TBJ_VERTEX:
return tet(pr)->saveVertexArray(w, map);
case geo_TBJ_ENTRIES:
break;
}
return false;
}
virtual bool saveField(const GA_Primitive *pr, int i,
UT_JSONValue &v, const GA_SaveMap &map) const
{
switch (i)
{
case geo_TBJ_VERTEX:
return false;
case geo_TBJ_ENTRIES:
break;
}
UT_ASSERT(0);
return false;
}
virtual bool loadField(GA_Primitive *pr, int i, UT_JSONParser &p,
const GA_LoadMap &map) const
{
switch (i)
{
case geo_TBJ_VERTEX:
return tet(pr)->loadVertexArray(p, map);
case geo_TBJ_ENTRIES:
break;
}
UT_ASSERT(0);
return false;
}
virtual bool loadField(GA_Primitive *pr, int i, UT_JSONParser &p,
const UT_JSONValue &v, const GA_LoadMap &map) const
{
switch (i)
{
case geo_TBJ_VERTEX:
return false;
case geo_TBJ_ENTRIES:
break;
}
UT_ASSERT(0);
return false;
}
virtual bool isEqual(int i, const GA_Primitive *p0,
const GA_Primitive *p1) const
{
switch (i)
{
case geo_TBJ_VERTEX:
return false;
case geo_TBJ_ENTRIES:
break;
}
UT_ASSERT(0);
return false;
}
private:
};
}
static const GA_PrimitiveJSON *
tetrahedronJSON()
{
static GA_PrimitiveJSON *theJSON = NULL;
if (!theJSON)
theJSON = new geo_PrimTetraJSON();
return theJSON;
}
const GA_PrimitiveJSON *
GEO_PrimTetra::getJSON() const
{
return tetrahedronJSON();
}
bool
GEO_PrimTetra::saveVertexArray(UT_JSONWriter &w,
const GA_SaveMap &map) const
{
if (!w.beginUniformArray(PT_PER_TET, UT_JID_INT32))
return false;
for (int i = 0; i < PT_PER_TET; i++)
{
int32 v = map.getIndex(fastVertexOffset(i), GA_ATTRIB_VERTEX);
if (!w.uniformWrite(v))
return false;
}
return w.endUniformArray();
}
bool
GEO_PrimTetra::loadVertexArray(UT_JSONParser &p, const GA_LoadMap &map)
{
int nvertex;
GA_Offset vtxoff = map.getVertexOffset();
nvertex = p.parseUniformArray(myVertexOffsets, PT_PER_TET);
for (int i = 0; i < nvertex; i++)
{
if (myVertexOffsets[i] >= 0)
myVertexOffsets[i] += vtxoff;
}
if (nvertex < PT_PER_TET)
return false;
return true;
}
int
GEO_PrimTetra::getBBox(UT_BoundingBox *bbox) const
{
int cnt;
bbox->initBounds(getDetail().getPos3(vertexPoint(0)));
for (cnt = PT_PER_TET-1; cnt > 0; cnt--)
{
bbox->enlargeBounds(getDetail().getPos3(vertexPoint(cnt)));
}
return 1;
}
UT_Vector3
GEO_PrimTetra::baryCenter() const
{
float x, y, z;
int i;
x = y = z = 0.0;
for (i=PT_PER_TET-1; i>=0; i--)
{
UT_Vector3 pos(getDetail().getPos3(vertexPoint(i)));
x += pos.x();
y += pos.y();
z += pos.z();
}
x /= PT_PER_TET;
y /= PT_PER_TET;
z /= PT_PER_TET;
return UT_Vector3(x, y, z);
}
bool
GEO_PrimTetra::isDegenerate() const
{
for (int i = 0; i < PT_PER_TET; i++)
{
for (int j = i+1; j < PT_PER_TET; j++)
{
if (vertexPoint(i) == vertexPoint(j))
return true;
}
}
return false;
}
void
GEO_PrimTetra::copyPrimitive(const GEO_Primitive *psrc, GEO_Point **ptredirect)
{
if (psrc == this) return;
const GEO_PrimTetra *src = (const GEO_PrimTetra *)psrc;
const GA_IndexMap &src_points = src->getParent()->getPointMap();
GEO_Point *ppt;
GA_VertexWrangler vertex_wrangler(*getParent(),
*src->getParent());
int i, n;
n = PT_PER_TET;
for (i=n-1; i>=0; i--)
{
GA_Offset v = fastVertexOffset(i);
ppt = ptredirect[src_points.indexFromOffset(src->vertexPoint(i))];
wireVertex(v, ppt ? ppt->getMapOffset() : GA_INVALID_OFFSET);
vertex_wrangler.copyAttributeValues(v, src->fastVertexOffset(i));
}
}
void
GEO_PrimTetra::copyOffsetPrimitive(const GEO_Primitive *psrc, int basept)
{
if (psrc == this) return;
const GEO_PrimTetra *src = (const GEO_PrimTetra *)psrc;
const GA_IndexMap &points = getParent()->getPointMap();
const GA_IndexMap &src_points = src->getParent()->getPointMap();
GA_Offset ppt;
int i, n;
GA_VertexWrangler vertex_wrangler(*getParent(),
*src->getParent());
n = PT_PER_TET;
for (i=n-1; i>=0; i--)
{
GA_Offset v = fastVertexOffset(i);
ppt = points.offsetFromIndex(
src_points.indexFromOffset(src->vertexPoint(i)) + basept);
wireVertex(v, ppt);
vertex_wrangler.copyAttributeValues(v, src->fastVertexOffset(i));
}
}
GEO_Primitive *
GEO_PrimTetra::copy(int preserve_shared_pts) const
{
GEO_Primitive *clone = GEO_Primitive::copy(preserve_shared_pts);
if (clone)
{
GEO_PrimTetra *face = (GEO_PrimTetra*)clone;
GA_Offset ppt;
GA_ElementWranglerCache wranglers(*getParent(),
GA_PointWrangler::INCLUDE_P);
int nvtx = getVertexCount();
int i;
if (preserve_shared_pts)
{
UT_SparseArray<GA_Offset *> addedpoints;
GA_Offset *ppt_ptr;
for (i = 0; i < nvtx; i++)
{
GA_Offset src_ppt = vertexPoint(i);
GA_Offset v = face->fastVertexOffset(i);
GA_Offset sv = fastVertexOffset(i);
if (!(ppt_ptr = addedpoints(src_ppt)))
{
ppt = getParent()->appendPointOffset();
wranglers.getPoint().copyAttributeValues(ppt, src_ppt);
addedpoints.append(src_ppt, new GA_Offset(ppt));
}
else
ppt = *ppt_ptr;
face->wireVertex(v, ppt);
wranglers.getVertex().copyAttributeValues(v, sv);
}
int dummy_index;
for (i = 0; i < addedpoints.entries(); i++)
delete (GA_Offset *)addedpoints.getRawEntry(i, dummy_index);
}
else
{
for (i = 0; i < nvtx; i++)
{
GA_Offset v = face->fastVertexOffset(i);
ppt = getParent()->appendPointOffset();
face->wireVertex(v, ppt);
wranglers.getPoint().copyAttributeValues(ppt, vertexPoint(i));
wranglers.getVertex().copyAttributeValues(v, fastVertexOffset(i));
}
}
}
return clone;
}
void
GEO_PrimTetra::copyUnwiredForMerge(const GA_Primitive *prim_src,
const GA_MergeMap &map)
{
UT_ASSERT( prim_src != this );
const GEO_PrimTetra *src = static_cast<const GEO_PrimTetra *>(
prim_src);
if (map.isIdentityMap(GA_ATTRIB_VERTEX))
{
for (int i = 0; i < PT_PER_TET; i++)
myVertexOffsets[i] = src->myVertexOffsets[i];
}
else
{
int n = PT_PER_TET;
for (int i = 0; i < n; i++)
{
GA_Offset sidx = src->fastVertexOffset(i);
myVertexOffsets[i] = map.mapDestFromSource(GA_ATTRIB_VERTEX, sidx);
}
}
}
void
GEO_PrimTetra::swapVertexOffsets(const GA_Defragment &defrag)
{
GA_Size nchanged = 0;
for (int i = 0; i < PT_PER_TET; i++)
{
GA_Offset v = fastVertexOffset(i);
if (defrag.hasOffsetChanged(v))
{
myVertexOffsets[i] = defrag.mapOffset(v);
nchanged++;
}
}
}