/*****************************************************************************
*
* Copyright (c) 2000 - 2018, Lawrence Livermore National Security, LLC
* Produced at the Lawrence Livermore National Laboratory
* LLNL-CODE-442911
* All rights reserved.
*
* This file is  part of VisIt. For  details, see https://visit.llnl.gov/.  The
* full copyright notice is contained in the file COPYRIGHT located at the root
* of the VisIt distribution or at http://www.llnl.gov/visit/copyright.html.
*
* Redistribution  and  use  in  source  and  binary  forms,  with  or  without
* modification, are permitted provided that the following conditions are met:
*
*  - Redistributions of  source code must  retain the above  copyright notice,
*    this list of conditions and the disclaimer below.
*  - Redistributions in binary form must reproduce the above copyright notice,
*    this  list of  conditions  and  the  disclaimer (as noted below)  in  the
*    documentation and/or other materials provided with the distribution.
*  - Neither the name of  the LLNS/LLNL nor the names of  its contributors may
*    be used to endorse or promote products derived from this software without
*    specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT  HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT  LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR  PURPOSE
* ARE  DISCLAIMED. IN  NO EVENT  SHALL LAWRENCE  LIVERMORE NATIONAL  SECURITY,
* LLC, THE  U.S.  DEPARTMENT OF  ENERGY  OR  CONTRIBUTORS BE  LIABLE  FOR  ANY
* DIRECT,  INDIRECT,   INCIDENTAL,   SPECIAL,   EXEMPLARY,  OR   CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT  LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR
* SERVICES; LOSS OF  USE, DATA, OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER
* CAUSED  AND  ON  ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT
* LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY  WAY
* OUT OF THE  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*****************************************************************************/

#include <SliceAttributes.h>
#include <DataNode.h>
#include <PlaneAttributes.h>

//
// Enum conversion methods for SliceAttributes::AxisType
//

static const char *AxisType_strings[] = {
"XAxis", "YAxis", "ZAxis", 
"Arbitrary", "ThetaPhi"};

std::string
SliceAttributes::AxisType_ToString(SliceAttributes::AxisType t)
{
    int index = int(t);
    if(index < 0 || index >= 5) index = 0;
    return AxisType_strings[index];
}

std::string
SliceAttributes::AxisType_ToString(int t)
{
    int index = (t < 0 || t >= 5) ? 0 : t;
    return AxisType_strings[index];
}

bool
SliceAttributes::AxisType_FromString(const std::string &s, SliceAttributes::AxisType &val)
{
    val = SliceAttributes::XAxis;
    for(int i = 0; i < 5; ++i)
    {
        if(s == AxisType_strings[i])
        {
            val = (AxisType)i;
            return true;
        }
    }
    return false;
}

//
// Enum conversion methods for SliceAttributes::OriginType
//

static const char *OriginType_strings[] = {
"Point", "Intercept", "Percent", 
"Zone", "Node"};

std::string
SliceAttributes::OriginType_ToString(SliceAttributes::OriginType t)
{
    int index = int(t);
    if(index < 0 || index >= 5) index = 0;
    return OriginType_strings[index];
}

std::string
SliceAttributes::OriginType_ToString(int t)
{
    int index = (t < 0 || t >= 5) ? 0 : t;
    return OriginType_strings[index];
}

bool
SliceAttributes::OriginType_FromString(const std::string &s, SliceAttributes::OriginType &val)
{
    val = SliceAttributes::Point;
    for(int i = 0; i < 5; ++i)
    {
        if(s == OriginType_strings[i])
        {
            val = (OriginType)i;
            return true;
        }
    }
    return false;
}

// ****************************************************************************
// Method: SliceAttributes::SliceAttributes
//
// Purpose: 
//   Init utility for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

void SliceAttributes::Init()
{
    originType = Intercept;
    originPoint[0] = 0;
    originPoint[1] = 0;
    originPoint[2] = 0;
    originIntercept = 0;
    originPercent = 0;
    originZone = 0;
    originNode = 0;
    normal[0] = 0;
    normal[1] = -1;
    normal[2] = 0;
    axisType = YAxis;
    upAxis[0] = 0;
    upAxis[1] = 0;
    upAxis[2] = 1;
    project2d = true;
    interactive = true;
    flip = false;
    originZoneDomain = 0;
    originNodeDomain = 0;
    meshName = "default";
    theta = 0;
    phi = 0;

    SliceAttributes::SelectAll();
}

// ****************************************************************************
// Method: SliceAttributes::SliceAttributes
//
// Purpose: 
//   Copy utility for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

void SliceAttributes::Copy(const SliceAttributes &obj)
{
    originType = obj.originType;
    originPoint[0] = obj.originPoint[0];
    originPoint[1] = obj.originPoint[1];
    originPoint[2] = obj.originPoint[2];

    originIntercept = obj.originIntercept;
    originPercent = obj.originPercent;
    originZone = obj.originZone;
    originNode = obj.originNode;
    normal[0] = obj.normal[0];
    normal[1] = obj.normal[1];
    normal[2] = obj.normal[2];

    axisType = obj.axisType;
    upAxis[0] = obj.upAxis[0];
    upAxis[1] = obj.upAxis[1];
    upAxis[2] = obj.upAxis[2];

    project2d = obj.project2d;
    interactive = obj.interactive;
    flip = obj.flip;
    originZoneDomain = obj.originZoneDomain;
    originNodeDomain = obj.originNodeDomain;
    meshName = obj.meshName;
    theta = obj.theta;
    phi = obj.phi;

    SliceAttributes::SelectAll();
}

// Type map format string
const char *SliceAttributes::TypeMapFormatString = SLICEATTRIBUTES_TMFS;
const AttributeGroup::private_tmfs_t SliceAttributes::TmfsStruct = {SLICEATTRIBUTES_TMFS};


// ****************************************************************************
// Method: SliceAttributes::SliceAttributes
//
// Purpose: 
//   Default constructor for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

SliceAttributes::SliceAttributes() : 
    AttributeSubject(SliceAttributes::TypeMapFormatString)
{
    SliceAttributes::Init();
}

// ****************************************************************************
// Method: SliceAttributes::SliceAttributes
//
// Purpose: 
//   Constructor for the derived classes of SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

SliceAttributes::SliceAttributes(private_tmfs_t tmfs) : 
    AttributeSubject(tmfs.tmfs)
{
    SliceAttributes::Init();
}

// ****************************************************************************
// Method: SliceAttributes::SliceAttributes
//
// Purpose: 
//   Copy constructor for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

SliceAttributes::SliceAttributes(const SliceAttributes &obj) : 
    AttributeSubject(SliceAttributes::TypeMapFormatString)
{
    SliceAttributes::Copy(obj);
}

// ****************************************************************************
// Method: SliceAttributes::SliceAttributes
//
// Purpose: 
//   Copy constructor for derived classes of the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

SliceAttributes::SliceAttributes(const SliceAttributes &obj, private_tmfs_t tmfs) : 
    AttributeSubject(tmfs.tmfs)
{
    SliceAttributes::Copy(obj);
}

// ****************************************************************************
// Method: SliceAttributes::~SliceAttributes
//
// Purpose: 
//   Destructor for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

SliceAttributes::~SliceAttributes()
{
    // nothing here
}

// ****************************************************************************
// Method: SliceAttributes::operator = 
//
// Purpose: 
//   Assignment operator for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

SliceAttributes& 
SliceAttributes::operator = (const SliceAttributes &obj)
{
    if (this == &obj) return *this;

    SliceAttributes::Copy(obj);

    return *this;
}

// ****************************************************************************
// Method: SliceAttributes::operator == 
//
// Purpose: 
//   Comparison operator == for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

bool
SliceAttributes::operator == (const SliceAttributes &obj) const
{
    // Compare the originPoint arrays.
    bool originPoint_equal = true;
    for(int i = 0; i < 3 && originPoint_equal; ++i)
        originPoint_equal = (originPoint[i] == obj.originPoint[i]);

    // Compare the normal arrays.
    bool normal_equal = true;
    for(int i = 0; i < 3 && normal_equal; ++i)
        normal_equal = (normal[i] == obj.normal[i]);

    // Compare the upAxis arrays.
    bool upAxis_equal = true;
    for(int i = 0; i < 3 && upAxis_equal; ++i)
        upAxis_equal = (upAxis[i] == obj.upAxis[i]);

    // Create the return value
    return ((originType == obj.originType) &&
            originPoint_equal &&
            (originIntercept == obj.originIntercept) &&
            (originPercent == obj.originPercent) &&
            (originZone == obj.originZone) &&
            (originNode == obj.originNode) &&
            normal_equal &&
            true /* can ignore axisType */ &&
            upAxis_equal &&
            (project2d == obj.project2d) &&
            (interactive == obj.interactive) &&
            true /* can ignore flip */ &&
            (originZoneDomain == obj.originZoneDomain) &&
            (originNodeDomain == obj.originNodeDomain) &&
            (meshName == obj.meshName) &&
            (theta == obj.theta) &&
            (phi == obj.phi));
}

// ****************************************************************************
// Method: SliceAttributes::operator != 
//
// Purpose: 
//   Comparison operator != for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

bool
SliceAttributes::operator != (const SliceAttributes &obj) const
{
    return !(this->operator == (obj));
}

// ****************************************************************************
// Method: SliceAttributes::TypeName
//
// Purpose: 
//   Type name method for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

const std::string
SliceAttributes::TypeName() const
{
    return "SliceAttributes";
}

// ****************************************************************************
// Method: SliceAttributes::CopyAttributes
//
// Purpose: 
//   CopyAttributes method for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   Fri Mar 15 15:24:44 PST 2002
//
// Modifications:
//    Jeremy Meredith, Fri Mar 15 15:47:24 PST 2002
//    Made origin be a Point.
//
//    Jeremy Meredith, Sun Nov 17 17:38:10 PST 2002
//    Updated for some orthogonal additions.
//
//    Jeremy Meredith, Mon May  5 15:01:30 PDT 2003
//    Changed the way origin works.
//
//    Dave Pugmire, Mon Oct 22 10:25:42 EDT 2007
//    Added theta-phi method of editing the plane normal.    
//
// ****************************************************************************

bool
SliceAttributes::CopyAttributes(const AttributeGroup *atts)
{
    bool retval = false;

    if(TypeName() == atts->TypeName())
    {
        // Call assignment operator.
        const SliceAttributes *tmp = (const SliceAttributes *)atts;
        *this = *tmp;
        retval = true;
    }
    else if(atts->TypeName() == "PlaneAttributes")
    {
        if(interactive)
        {
            const PlaneAttributes *tmp = (const PlaneAttributes *)atts;
            SetOriginPoint(tmp->GetOrigin());
            SetOriginType(Point);
            SetNormal(tmp->GetNormal());
            SetUpAxis(tmp->GetUpAxis());
            if ( GetAxisType() != Arbitrary && GetAxisType() != ThetaPhi )
                SetAxisType(Arbitrary);

            //Set theta/phi.
            double n[3] = {tmp->GetNormal()[0],tmp->GetNormal()[1],tmp->GetNormal()[2]};
            double len = sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
            n[0] /= len;
            n[1] /= len;
            n[2] /= len;
            len = 1.0;
            double theta = atan2( n[1], n[0] ) * 57.29577951308232;
            double phi = acos( n[2] / len ) * 57.29577951308232;
            theta -= 90;
            phi -= 90;
            phi = - phi;

            theta = (fabs(theta) < 1e-5 ? 0 : theta);
            phi = (fabs(phi) < 1e-5 ? 0 : phi);
            SetTheta( theta );
            SetPhi( phi );

            retval = true;
        }
    }

    return retval;
}

// ****************************************************************************
// Method: SliceAttributes::CreateCompatible
//
// Purpose: 
//   Creates an object of the specified type initialized with the attributes
//   from this object.
//
// Arguments:
//   tname : The typename of the object that we want to create.
//
// Returns:    A new object of the type specified by tname or 0.
//
// Note:       
//
// Programmer: Brad Whitlock
// Creation:   Mon Feb 11 15:36:32 PST 2002
//
// Modifications:
//    Jeremy Meredith, Fri Mar 15 15:42:27 PST 2002
//    Made origin be a Point.
//   
//    Jeremy Meredith, Mon May  5 15:01:30 PDT 2003
//    Changed the way origin works.
//
//    Kathleen Bonnell, Thu Jul  3 11:18:43 PDT 2003 
//    Set 'threeSpace' variable in PlaneAtts from 'project2d'. 
//
//    Gunther H. Weber, Wed Aug 15 16:30:08 PDT 2007
//    Update plane tool correctly if in fixed axis and/or intercept
//    mode. Percentage, node and zone still won't work since
//    viewer does not have access to information necessary for that.
//
//    Dave Pugmire, Thu Oct 18 08:25:42 EDT 2007
//    Added theta-phi method of editing the plane normal.
//
// ****************************************************************************

AttributeSubject *
SliceAttributes::CreateCompatible(const std::string &tname) const
{
    AttributeSubject *retval = 0;

    if(TypeName() == tname)
    {
        retval = new SliceAttributes(*this);
    }
    else if(tname == "PlaneAttributes")
    {
        PlaneAttributes *p = new PlaneAttributes;

        double nx = 0.;
        double ny = 0.;
        double nz = 0.;

        switch (GetAxisType())
        {
            case XAxis:
                nx = 1.;
                break;
            case YAxis:
                ny = 1.;
                break;
            case ZAxis:
                nz = 1.;
                break;
            case Arbitrary:
            case ThetaPhi:
                nx = GetNormal()[0];
                ny = GetNormal()[1];
                nz = GetNormal()[2];
                break;
        }
        double normal[3] = { nx, ny, nz };
        p->SetNormal(normal);

        if (GetOriginType() == Intercept)
        {
            double nl = std::sqrt(nx*nx + ny*ny + nz*nz);

            // We want to make sure for orthogonal slices that "intercept" is still
            // meaningful even when the normal is pointing in the negative direction
            if (nx+ny+nz < 0 && GetAxisType() != Arbitrary)
            {
                nl *= -1;
            }

            double d = GetOriginIntercept();

            double o[3];
            o[0] = nx * d / nl;
            o[1] = ny * d / nl;
            o[2] = nz * d / nl;

            p->SetOrigin(o);
        }
        else
        {
            p->SetOrigin(GetOriginPoint());
        }

        p->SetUpAxis(GetUpAxis());
        p->SetThreeSpace(!GetProject2d());
        retval = p;
    }

    return retval;
}

// ****************************************************************************
// Method: SliceAttributes::NewInstance
//
// Purpose: 
//   NewInstance method for the SliceAttributes class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

AttributeSubject *
SliceAttributes::NewInstance(bool copy) const
{
    AttributeSubject *retval = 0;
    if(copy)
        retval = new SliceAttributes(*this);
    else
        retval = new SliceAttributes;

    return retval;
}

// ****************************************************************************
// Method: SliceAttributes::SelectAll
//
// Purpose: 
//   Selects all attributes.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

void
SliceAttributes::SelectAll()
{
    Select(ID_originType,       (void *)&originType);
    Select(ID_originPoint,      (void *)originPoint, 3);
    Select(ID_originIntercept,  (void *)&originIntercept);
    Select(ID_originPercent,    (void *)&originPercent);
    Select(ID_originZone,       (void *)&originZone);
    Select(ID_originNode,       (void *)&originNode);
    Select(ID_normal,           (void *)normal, 3);
    Select(ID_axisType,         (void *)&axisType);
    Select(ID_upAxis,           (void *)upAxis, 3);
    Select(ID_project2d,        (void *)&project2d);
    Select(ID_interactive,      (void *)&interactive);
    Select(ID_flip,             (void *)&flip);
    Select(ID_originZoneDomain, (void *)&originZoneDomain);
    Select(ID_originNodeDomain, (void *)&originNodeDomain);
    Select(ID_meshName,         (void *)&meshName);
    Select(ID_theta,            (void *)&theta);
    Select(ID_phi,              (void *)&phi);
}

///////////////////////////////////////////////////////////////////////////////
// Persistence methods
///////////////////////////////////////////////////////////////////////////////

// ****************************************************************************
// Method: SliceAttributes::CreateNode
//
// Purpose: 
//   This method creates a DataNode representation of the object so it can be saved to a config file.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

bool
SliceAttributes::CreateNode(DataNode *parentNode, bool completeSave, bool forceAdd)
{
    if(parentNode == 0)
        return false;

    SliceAttributes defaultObject;
    bool addToParent = false;
    // Create a node for SliceAttributes.
    DataNode *node = new DataNode("SliceAttributes");

    if(completeSave || !FieldsEqual(ID_originType, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originType", OriginType_ToString(originType)));
    }

    if(completeSave || !FieldsEqual(ID_originPoint, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originPoint", originPoint, 3));
    }

    if(completeSave || !FieldsEqual(ID_originIntercept, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originIntercept", originIntercept));
    }

    if(completeSave || !FieldsEqual(ID_originPercent, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originPercent", originPercent));
    }

    if(completeSave || !FieldsEqual(ID_originZone, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originZone", originZone));
    }

    if(completeSave || !FieldsEqual(ID_originNode, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originNode", originNode));
    }

    if(completeSave || !FieldsEqual(ID_normal, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("normal", normal, 3));
    }

    if(completeSave || !FieldsEqual(ID_axisType, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("axisType", AxisType_ToString(axisType)));
    }

    if(completeSave || !FieldsEqual(ID_upAxis, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("upAxis", upAxis, 3));
    }

    if(completeSave || !FieldsEqual(ID_project2d, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("project2d", project2d));
    }

    if(completeSave || !FieldsEqual(ID_interactive, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("interactive", interactive));
    }

    if(completeSave || !FieldsEqual(ID_flip, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("flip", flip));
    }

    if(completeSave || !FieldsEqual(ID_originZoneDomain, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originZoneDomain", originZoneDomain));
    }

    if(completeSave || !FieldsEqual(ID_originNodeDomain, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("originNodeDomain", originNodeDomain));
    }

    if(completeSave || !FieldsEqual(ID_meshName, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("meshName", meshName));
    }

    if(completeSave || !FieldsEqual(ID_theta, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("theta", theta));
    }

    if(completeSave || !FieldsEqual(ID_phi, &defaultObject))
    {
        addToParent = true;
        node->AddNode(new DataNode("phi", phi));
    }


    // Add the node to the parent node.
    if(addToParent || forceAdd)
        parentNode->AddNode(node);
    else
        delete node;

    return (addToParent || forceAdd);
}

// ****************************************************************************
// Method: SliceAttributes::SetFromNode
//
// Purpose: 
//   This method sets attributes in this object from values in a DataNode representation of the object.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

void
SliceAttributes::SetFromNode(DataNode *parentNode)
{
    if(parentNode == 0)
        return;

    DataNode *searchNode = parentNode->GetNode("SliceAttributes");
    if(searchNode == 0)
        return;

    DataNode *node;
    if((node = searchNode->GetNode("originType")) != 0)
    {
        // Allow enums to be int or string in the config file
        if(node->GetNodeType() == INT_NODE)
        {
            int ival = node->AsInt();
            if(ival >= 0 && ival < 5)
                SetOriginType(OriginType(ival));
        }
        else if(node->GetNodeType() == STRING_NODE)
        {
            OriginType value;
            if(OriginType_FromString(node->AsString(), value))
                SetOriginType(value);
        }
    }
    if((node = searchNode->GetNode("originPoint")) != 0)
        SetOriginPoint(node->AsDoubleArray());
    if((node = searchNode->GetNode("originIntercept")) != 0)
        SetOriginIntercept(node->AsDouble());
    if((node = searchNode->GetNode("originPercent")) != 0)
        SetOriginPercent(node->AsDouble());
    if((node = searchNode->GetNode("originZone")) != 0)
        SetOriginZone(node->AsInt());
    if((node = searchNode->GetNode("originNode")) != 0)
        SetOriginNode(node->AsInt());
    if((node = searchNode->GetNode("normal")) != 0)
        SetNormal(node->AsDoubleArray());
    if((node = searchNode->GetNode("axisType")) != 0)
    {
        // Allow enums to be int or string in the config file
        if(node->GetNodeType() == INT_NODE)
        {
            int ival = node->AsInt();
            if(ival >= 0 && ival < 5)
                SetAxisType(AxisType(ival));
        }
        else if(node->GetNodeType() == STRING_NODE)
        {
            AxisType value;
            if(AxisType_FromString(node->AsString(), value))
                SetAxisType(value);
        }
    }
    if((node = searchNode->GetNode("upAxis")) != 0)
        SetUpAxis(node->AsDoubleArray());
    if((node = searchNode->GetNode("project2d")) != 0)
        SetProject2d(node->AsBool());
    if((node = searchNode->GetNode("interactive")) != 0)
        SetInteractive(node->AsBool());
    if((node = searchNode->GetNode("flip")) != 0)
        SetFlip(node->AsBool());
    if((node = searchNode->GetNode("originZoneDomain")) != 0)
        SetOriginZoneDomain(node->AsInt());
    if((node = searchNode->GetNode("originNodeDomain")) != 0)
        SetOriginNodeDomain(node->AsInt());
    if((node = searchNode->GetNode("meshName")) != 0)
        SetMeshName(node->AsString());
    if((node = searchNode->GetNode("theta")) != 0)
        SetTheta(node->AsDouble());
    if((node = searchNode->GetNode("phi")) != 0)
        SetPhi(node->AsDouble());
}

///////////////////////////////////////////////////////////////////////////////
// Set property methods
///////////////////////////////////////////////////////////////////////////////

void
SliceAttributes::SetOriginType(SliceAttributes::OriginType originType_)
{
    originType = originType_;
    Select(ID_originType, (void *)&originType);
}

void
SliceAttributes::SetOriginPoint(const double *originPoint_)
{
    originPoint[0] = originPoint_[0];
    originPoint[1] = originPoint_[1];
    originPoint[2] = originPoint_[2];
    Select(ID_originPoint, (void *)originPoint, 3);
}

void
SliceAttributes::SetOriginIntercept(double originIntercept_)
{
    originIntercept = originIntercept_;
    Select(ID_originIntercept, (void *)&originIntercept);
}

void
SliceAttributes::SetOriginPercent(double originPercent_)
{
    originPercent = originPercent_;
    Select(ID_originPercent, (void *)&originPercent);
}

void
SliceAttributes::SetOriginZone(int originZone_)
{
    originZone = originZone_;
    Select(ID_originZone, (void *)&originZone);
}

void
SliceAttributes::SetOriginNode(int originNode_)
{
    originNode = originNode_;
    Select(ID_originNode, (void *)&originNode);
}

void
SliceAttributes::SetNormal(const double *normal_)
{
    normal[0] = normal_[0];
    normal[1] = normal_[1];
    normal[2] = normal_[2];
    Select(ID_normal, (void *)normal, 3);
}

void
SliceAttributes::SetAxisType(SliceAttributes::AxisType axisType_)
{
    axisType = axisType_;
    Select(ID_axisType, (void *)&axisType);
    UpdateOrthogonalAxes();

}

void
SliceAttributes::SetUpAxis(const double *upAxis_)
{
    upAxis[0] = upAxis_[0];
    upAxis[1] = upAxis_[1];
    upAxis[2] = upAxis_[2];
    Select(ID_upAxis, (void *)upAxis, 3);
}

void
SliceAttributes::SetProject2d(bool project2d_)
{
    project2d = project2d_;
    Select(ID_project2d, (void *)&project2d);
}

void
SliceAttributes::SetInteractive(bool interactive_)
{
    interactive = interactive_;
    Select(ID_interactive, (void *)&interactive);
}

void
SliceAttributes::SetFlip(bool flip_)
{
    flip = flip_;
    Select(ID_flip, (void *)&flip);
    UpdateOrthogonalAxes();

}

void
SliceAttributes::SetOriginZoneDomain(int originZoneDomain_)
{
    originZoneDomain = originZoneDomain_;
    Select(ID_originZoneDomain, (void *)&originZoneDomain);
}

void
SliceAttributes::SetOriginNodeDomain(int originNodeDomain_)
{
    originNodeDomain = originNodeDomain_;
    Select(ID_originNodeDomain, (void *)&originNodeDomain);
}

void
SliceAttributes::SetMeshName(const std::string &meshName_)
{
    meshName = meshName_;
    Select(ID_meshName, (void *)&meshName);
}

void
SliceAttributes::SetTheta(double theta_)
{
    theta = theta_;
    Select(ID_theta, (void *)&theta);
}

void
SliceAttributes::SetPhi(double phi_)
{
    phi = phi_;
    Select(ID_phi, (void *)&phi);
}

///////////////////////////////////////////////////////////////////////////////
// Get property methods
///////////////////////////////////////////////////////////////////////////////

SliceAttributes::OriginType
SliceAttributes::GetOriginType() const
{
    return OriginType(originType);
}

const double *
SliceAttributes::GetOriginPoint() const
{
    return originPoint;
}

double *
SliceAttributes::GetOriginPoint()
{
    return originPoint;
}

double
SliceAttributes::GetOriginIntercept() const
{
    return originIntercept;
}

double
SliceAttributes::GetOriginPercent() const
{
    return originPercent;
}

int
SliceAttributes::GetOriginZone() const
{
    return originZone;
}

int
SliceAttributes::GetOriginNode() const
{
    return originNode;
}

const double *
SliceAttributes::GetNormal() const
{
    return normal;
}

double *
SliceAttributes::GetNormal()
{
    return normal;
}

SliceAttributes::AxisType
SliceAttributes::GetAxisType() const
{
    return AxisType(axisType);
}

const double *
SliceAttributes::GetUpAxis() const
{
    return upAxis;
}

double *
SliceAttributes::GetUpAxis()
{
    return upAxis;
}

bool
SliceAttributes::GetProject2d() const
{
    return project2d;
}

bool
SliceAttributes::GetInteractive() const
{
    return interactive;
}

bool
SliceAttributes::GetFlip() const
{
    return flip;
}

int
SliceAttributes::GetOriginZoneDomain() const
{
    return originZoneDomain;
}

int
SliceAttributes::GetOriginNodeDomain() const
{
    return originNodeDomain;
}

const std::string &
SliceAttributes::GetMeshName() const
{
    return meshName;
}

std::string &
SliceAttributes::GetMeshName()
{
    return meshName;
}

double
SliceAttributes::GetTheta() const
{
    return theta;
}

double
SliceAttributes::GetPhi() const
{
    return phi;
}

///////////////////////////////////////////////////////////////////////////////
// Select property methods
///////////////////////////////////////////////////////////////////////////////

void
SliceAttributes::SelectOriginPoint()
{
    Select(ID_originPoint, (void *)originPoint, 3);
}

void
SliceAttributes::SelectNormal()
{
    Select(ID_normal, (void *)normal, 3);
}

void
SliceAttributes::SelectUpAxis()
{
    Select(ID_upAxis, (void *)upAxis, 3);
}

void
SliceAttributes::SelectMeshName()
{
    Select(ID_meshName, (void *)&meshName);
}

///////////////////////////////////////////////////////////////////////////////
// Keyframing methods
///////////////////////////////////////////////////////////////////////////////

// ****************************************************************************
// Method: SliceAttributes::GetFieldName
//
// Purpose: 
//   This method returns the name of a field given its index.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

std::string
SliceAttributes::GetFieldName(int index) const
{
    switch (index)
    {
    case ID_originType:       return "originType";
    case ID_originPoint:      return "originPoint";
    case ID_originIntercept:  return "originIntercept";
    case ID_originPercent:    return "originPercent";
    case ID_originZone:       return "originZone";
    case ID_originNode:       return "originNode";
    case ID_normal:           return "normal";
    case ID_axisType:         return "axisType";
    case ID_upAxis:           return "upAxis";
    case ID_project2d:        return "project2d";
    case ID_interactive:      return "interactive";
    case ID_flip:             return "flip";
    case ID_originZoneDomain: return "originZoneDomain";
    case ID_originNodeDomain: return "originNodeDomain";
    case ID_meshName:         return "meshName";
    case ID_theta:            return "theta";
    case ID_phi:              return "phi";
    default:  return "invalid index";
    }
}

// ****************************************************************************
// Method: SliceAttributes::GetFieldType
//
// Purpose: 
//   This method returns the type of a field given its index.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

AttributeGroup::FieldType
SliceAttributes::GetFieldType(int index) const
{
    switch (index)
    {
    case ID_originType:       return FieldType_enum;
    case ID_originPoint:      return FieldType_doubleArray;
    case ID_originIntercept:  return FieldType_double;
    case ID_originPercent:    return FieldType_double;
    case ID_originZone:       return FieldType_int;
    case ID_originNode:       return FieldType_int;
    case ID_normal:           return FieldType_doubleArray;
    case ID_axisType:         return FieldType_enum;
    case ID_upAxis:           return FieldType_doubleArray;
    case ID_project2d:        return FieldType_bool;
    case ID_interactive:      return FieldType_bool;
    case ID_flip:             return FieldType_bool;
    case ID_originZoneDomain: return FieldType_int;
    case ID_originNodeDomain: return FieldType_int;
    case ID_meshName:         return FieldType_string;
    case ID_theta:            return FieldType_double;
    case ID_phi:              return FieldType_double;
    default:  return FieldType_unknown;
    }
}

// ****************************************************************************
// Method: SliceAttributes::GetFieldTypeName
//
// Purpose: 
//   This method returns the name of a field type given its index.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

std::string
SliceAttributes::GetFieldTypeName(int index) const
{
    switch (index)
    {
    case ID_originType:       return "enum";
    case ID_originPoint:      return "doubleArray";
    case ID_originIntercept:  return "double";
    case ID_originPercent:    return "double";
    case ID_originZone:       return "int";
    case ID_originNode:       return "int";
    case ID_normal:           return "doubleArray";
    case ID_axisType:         return "enum";
    case ID_upAxis:           return "doubleArray";
    case ID_project2d:        return "bool";
    case ID_interactive:      return "bool";
    case ID_flip:             return "bool";
    case ID_originZoneDomain: return "int";
    case ID_originNodeDomain: return "int";
    case ID_meshName:         return "string";
    case ID_theta:            return "double";
    case ID_phi:              return "double";
    default:  return "invalid index";
    }
}

// ****************************************************************************
// Method: SliceAttributes::FieldsEqual
//
// Purpose: 
//   This method compares two fields and return true if they are equal.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//   
// ****************************************************************************

bool
SliceAttributes::FieldsEqual(int index_, const AttributeGroup *rhs) const
{
    const SliceAttributes &obj = *((const SliceAttributes*)rhs);
    bool retval = false;
    switch (index_)
    {
    case ID_originType:
        {  // new scope
        retval = (originType == obj.originType);
        }
        break;
    case ID_originPoint:
        {  // new scope
        // Compare the originPoint arrays.
        bool originPoint_equal = true;
        for(int i = 0; i < 3 && originPoint_equal; ++i)
            originPoint_equal = (originPoint[i] == obj.originPoint[i]);

        retval = originPoint_equal;
        }
        break;
    case ID_originIntercept:
        {  // new scope
        retval = (originIntercept == obj.originIntercept);
        }
        break;
    case ID_originPercent:
        {  // new scope
        retval = (originPercent == obj.originPercent);
        }
        break;
    case ID_originZone:
        {  // new scope
        retval = (originZone == obj.originZone);
        }
        break;
    case ID_originNode:
        {  // new scope
        retval = (originNode == obj.originNode);
        }
        break;
    case ID_normal:
        {  // new scope
        // Compare the normal arrays.
        bool normal_equal = true;
        for(int i = 0; i < 3 && normal_equal; ++i)
            normal_equal = (normal[i] == obj.normal[i]);

        retval = normal_equal;
        }
        break;
    case ID_axisType:
        {  // new scope
        retval = (axisType == obj.axisType);
        }
        break;
    case ID_upAxis:
        {  // new scope
        // Compare the upAxis arrays.
        bool upAxis_equal = true;
        for(int i = 0; i < 3 && upAxis_equal; ++i)
            upAxis_equal = (upAxis[i] == obj.upAxis[i]);

        retval = upAxis_equal;
        }
        break;
    case ID_project2d:
        {  // new scope
        retval = (project2d == obj.project2d);
        }
        break;
    case ID_interactive:
        {  // new scope
        retval = (interactive == obj.interactive);
        }
        break;
    case ID_flip:
        {  // new scope
        retval = (flip == obj.flip);
        }
        break;
    case ID_originZoneDomain:
        {  // new scope
        retval = (originZoneDomain == obj.originZoneDomain);
        }
        break;
    case ID_originNodeDomain:
        {  // new scope
        retval = (originNodeDomain == obj.originNodeDomain);
        }
        break;
    case ID_meshName:
        {  // new scope
        retval = (meshName == obj.meshName);
        }
        break;
    case ID_theta:
        {  // new scope
        retval = (theta == obj.theta);
        }
        break;
    case ID_phi:
        {  // new scope
        retval = (phi == obj.phi);
        }
        break;
    default: retval = false;
    }

    return retval;
}

///////////////////////////////////////////////////////////////////////////////
// User-defined methods.
///////////////////////////////////////////////////////////////////////////////

// ****************************************************************************
//  Method:  SliceAttributes::UpdateOrthogonalAxes
//
//  Purpose:
//    Updates upaxis and normal for an orthogonal axis.
//
//  Programmer:  Jeremy Meredith
//  Creation:    November 17, 2002
//
//  Modifications:
//    Jeremy Meredith, Mon May  5 15:01:59 PDT 2003
//    Re-ordered some fields, so the select calls needed to be changed here.
//
//    Kathleen Bonnell, Tue Jul  8 20:21:23 PDT 2003 
//    Slice now uses right-hand system when creating projection matrix.
//    Updated normals and upAxis to conform (with help from Jeremy). 
//
//    Dave Pugmire, Thu Oct 18 08:25:42 EDT 2007
//    Added theta-phi method of editing the plane normal.
//
// ****************************************************************************
void
SliceAttributes::UpdateOrthogonalAxes()
{
    if (axisType == Arbitrary || axisType == ThetaPhi)
        return;

    switch(axisType)
    {
      case XAxis:
        if (!flip)
        {
            // X: Y vs Z
            normal[0] = -1.; normal[1] =  0.; normal[2] =  0.;
            upAxis[0] =  0.; upAxis[1] =  1.; upAxis[2] =  0.;
            theta = 90;
            phi = 0;
        }
        else
        {
            // X: Z vs Y
            normal[0] =  1.; normal[1] =  0.; normal[2] =  0.;
            upAxis[0] =  0.; upAxis[1] =  0.; upAxis[2] =  1.;
            theta = -90;
            phi = 0;
        }
        break;
      case YAxis:
        if (!flip)
        {
            // Y: Z vs X
            normal[0] =  0.; normal[1] = -1.; normal[2] =  0.;
            upAxis[0] =  0.; upAxis[1] =  0.; upAxis[2] =  1.;
            theta = 180;
            phi = 0;
        }
        else
        {
            // Y: X vs Z
            normal[0] =  0.; normal[1] =  1.; normal[2] =  0.;
            upAxis[0] =  1.; upAxis[1] =  0.; upAxis[2] =  0.;
            theta = 0;
            phi = 0;
        }
        break;
      case ZAxis:
        if (!flip)
        {
            // Z: Y vs X
            normal[0] =  0.; normal[1] =  0.; normal[2] =  1.;
            upAxis[0] =  0.; upAxis[1] =  1.; upAxis[2] =  0.;
            theta = 0;
            phi = 90;
        }
        else
        {
            // Z: X vs Y
            normal[0] =  0.; normal[1] =  0.; normal[2] = -1.;
            upAxis[0] =  1.; upAxis[1] =  0.; upAxis[2] =  0.;
            theta = 0;
            phi = -90;
        }
        break;
      default:
        break;
    }

    Select(6, (void *)normal, 3);
    Select(8, (void *)upAxis, 3);
}

// ****************************************************************************
// Method: SliceAttributes::EqualTo
//
// Purpose: 
//     Determines if the slice attributes are equal to another attributes
//     object.
//
// Arguments:
//   atts : An attribute group.
//
// Returns:    True if this object is equal to atts, false otherwise.
//
// Note:       
//
// Programmer: Hank Childs
// Creation:   August 28, 2007
//
// Modifications:
//    Dave Pugmire, Thu Oct 18 08:25:42 EDT 2007
//    Added theta-phi method of editing the plane normal.
//
// ****************************************************************************

bool
SliceAttributes::EqualTo(const AttributeGroup *atts) const
{
    if ((atts->TypeName() != "PlaneAttributes") || !interactive)
        return AttributeSubject::EqualTo(atts);

    if (GetAxisType() != Arbitrary)
        return false;
    if (GetAxisType() != ThetaPhi)
        return false;
    if (GetOriginType() != Point)
        return false;

    const PlaneAttributes *tmp = (const PlaneAttributes *) atts;

    if (GetOriginPoint()[0] != tmp->GetOrigin()[0])
        return false;
    if (GetOriginPoint()[1] != tmp->GetOrigin()[1])
        return false;
    if (GetOriginPoint()[2] != tmp->GetOrigin()[2])
        return false;

    if (GetNormal()[0] != tmp->GetNormal()[0])
        return false;
    if (GetNormal()[1] != tmp->GetNormal()[1])
        return false;
    if (GetNormal()[2] != tmp->GetNormal()[2])
        return false;

    if (GetUpAxis()[0] != tmp->GetUpAxis()[0])
        return false;
    if (GetUpAxis()[1] != tmp->GetUpAxis()[1])
        return false;
    if (GetUpAxis()[2] != tmp->GetUpAxis()[2])
        return false;

    return true;
}

bool
SliceAttributes::SetValue(const std::string &name, const int &value)
{
    int index = FieldNameToIndex(name);
    bool retval;
    if(index == ID_axisType)
    {
        SetAxisType((AxisType)value);
        retval = true;
    }
    else
        retval = AttributeSubject::SetValue(name, value);
    return retval;
}

bool
SliceAttributes::SetValue(const std::string &name, const bool &value)
{
    int index = FieldNameToIndex(name);
    bool retval;
    if(index == ID_flip)
    {
        SetFlip(value);
        retval = true;
    }
    else
        retval = AttributeSubject::SetValue(name, value);
    return retval;
}

