tetprim/GEO_PrimTetra.C

/*
 * PROPRIETARY INFORMATION.  This software is proprietary to
 * Side Effects Software Inc., and is not to be reproduced,
 * transmitted, or disclosed in any way without written permission.
 *
 * Produced by:
 *      Jeff Lait
 *      Side Effects Software Inc
 *      477 Richmond Street West
 *      Toronto, Ontario
 *      Canada   M5V 3E7
 *      416-504-9876
 *
 * NAME:        GEO_PrimTetra.C ( GEO Library, C++)
 *
 * COMMENTS:    Base class for tetrahedrons.
 */

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

// So it doesn't look like a magic number
#define PT_PER_TET      4


GEO_PrimTetra::GEO_PrimTetra(GEO_Detail *d, GA_Offset offset)
    : GEO_Primitive(d, offset)
{
    // Nothing, but cannot be inlined.
    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)
{
    // NB: Base class must be unstashed before we can call allocateVertex().
    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));


    // Measure signed volume of pyramid (v0,v1,v2,v3) 
    float signedvol = -(v3-v0).dot(cross(v1-v0, v2-v0));

    signedvol /= 6;

    return signedvol;
}

fpreal
GEO_PrimTetra::calcArea() const
{
    float               area;           // Signed 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
{
    // Meaningless?
    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;
}

///
/// JSON methods
///

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
{
    // Duplicate points means degenerate.
    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;
}

///
// Methods to handle vertex attributes for the attribute dictionary
///
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;


    // TODO: Well and good to reuse the attribute handle for all our
    //       points/vertices, but we should do so across primitives
    //       as well.
    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;

    // TODO: Well and good to reuse the attribute handle for all our
    //       points/vertices, but we should do so across primitives
    //       as well.
    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;

        // TODO: Well and good to reuse the attribute handle for all our
        //       points/vertices, but we should do so across primitives
        //       as well.
        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++)
        {
            // Get source index
            GA_Offset sidx = src->fastVertexOffset(i);
            // Map to dest
            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++;
        }
    }
}

Generated on Thu Jan 31 00:24:23 2013 for HDK by  doxygen 1.5.9