/*****************************************************************************
*
* 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 <View3DAttributes.h>
#include <DataNode.h>
#include <math.h>

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

void View3DAttributes::Init()
{
    viewNormal[0] = 0;
    viewNormal[1] = 0;
    viewNormal[2] = 1;
    focus[0] = 0;
    focus[1] = 0;
    focus[2] = 0;
    viewUp[0] = 0;
    viewUp[1] = 1;
    viewUp[2] = 0;
    viewAngle = 30;
    parallelScale = 1;
    nearPlane = 0.001;
    farPlane = 100;
    imagePan[0] = 0;
    imagePan[1] = 0;
    imageZoom = 1;
    perspective = true;
    eyeAngle = 2;
    centerOfRotationSet = false;
    centerOfRotation[0] = 0;
    centerOfRotation[1] = 0;
    centerOfRotation[2] = 0;
    axis3DScaleFlag = false;
    axis3DScales[0] = 1;
    axis3DScales[1] = 1;
    axis3DScales[2] = 1;
    shear[0] = 0;
    shear[1] = 0;
    shear[2] = 1;
    windowValid = false;

    View3DAttributes::SelectAll();
}

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

void View3DAttributes::Copy(const View3DAttributes &obj)
{
    viewNormal[0] = obj.viewNormal[0];
    viewNormal[1] = obj.viewNormal[1];
    viewNormal[2] = obj.viewNormal[2];

    focus[0] = obj.focus[0];
    focus[1] = obj.focus[1];
    focus[2] = obj.focus[2];

    viewUp[0] = obj.viewUp[0];
    viewUp[1] = obj.viewUp[1];
    viewUp[2] = obj.viewUp[2];

    viewAngle = obj.viewAngle;
    parallelScale = obj.parallelScale;
    nearPlane = obj.nearPlane;
    farPlane = obj.farPlane;
    imagePan[0] = obj.imagePan[0];
    imagePan[1] = obj.imagePan[1];

    imageZoom = obj.imageZoom;
    perspective = obj.perspective;
    eyeAngle = obj.eyeAngle;
    centerOfRotationSet = obj.centerOfRotationSet;
    centerOfRotation[0] = obj.centerOfRotation[0];
    centerOfRotation[1] = obj.centerOfRotation[1];
    centerOfRotation[2] = obj.centerOfRotation[2];

    axis3DScaleFlag = obj.axis3DScaleFlag;
    axis3DScales[0] = obj.axis3DScales[0];
    axis3DScales[1] = obj.axis3DScales[1];
    axis3DScales[2] = obj.axis3DScales[2];

    shear[0] = obj.shear[0];
    shear[1] = obj.shear[1];
    shear[2] = obj.shear[2];

    windowValid = obj.windowValid;

    View3DAttributes::SelectAll();
}

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


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

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

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

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

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

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

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

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

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

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

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

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

    View3DAttributes::Copy(obj);

    return *this;
}

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

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

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

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

    // Compare the imagePan arrays.
    bool imagePan_equal = true;
    for(int i = 0; i < 2 && imagePan_equal; ++i)
        imagePan_equal = (imagePan[i] == obj.imagePan[i]);

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

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

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

    // Create the return value
    return (viewNormal_equal &&
            focus_equal &&
            viewUp_equal &&
            (viewAngle == obj.viewAngle) &&
            (parallelScale == obj.parallelScale) &&
            (nearPlane == obj.nearPlane) &&
            (farPlane == obj.farPlane) &&
            imagePan_equal &&
            (imageZoom == obj.imageZoom) &&
            (perspective == obj.perspective) &&
            (eyeAngle == obj.eyeAngle) &&
            (centerOfRotationSet == obj.centerOfRotationSet) &&
            centerOfRotation_equal &&
            (axis3DScaleFlag == obj.axis3DScaleFlag) &&
            axis3DScales_equal &&
            shear_equal &&
            (windowValid == obj.windowValid));
}

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

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

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

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

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

bool
View3DAttributes::CopyAttributes(const AttributeGroup *atts)
{
    if(TypeName() != atts->TypeName())
        return false;

    // Call assignment operator.
    const View3DAttributes *tmp = (const View3DAttributes *)atts;
    *this = *tmp;

    return true;
}

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

AttributeSubject *
View3DAttributes::CreateCompatible(const std::string &tname) const
{
    AttributeSubject *retval = 0;
    if(TypeName() == tname)
        retval = new View3DAttributes(*this);
    // Other cases could go here too. 

    return retval;
}

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

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

    return retval;
}

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

void
View3DAttributes::SelectAll()
{
    Select(ID_viewNormal,          (void *)viewNormal, 3);
    Select(ID_focus,               (void *)focus, 3);
    Select(ID_viewUp,              (void *)viewUp, 3);
    Select(ID_viewAngle,           (void *)&viewAngle);
    Select(ID_parallelScale,       (void *)&parallelScale);
    Select(ID_nearPlane,           (void *)&nearPlane);
    Select(ID_farPlane,            (void *)&farPlane);
    Select(ID_imagePan,            (void *)imagePan, 2);
    Select(ID_imageZoom,           (void *)&imageZoom);
    Select(ID_perspective,         (void *)&perspective);
    Select(ID_eyeAngle,            (void *)&eyeAngle);
    Select(ID_centerOfRotationSet, (void *)&centerOfRotationSet);
    Select(ID_centerOfRotation,    (void *)centerOfRotation, 3);
    Select(ID_axis3DScaleFlag,     (void *)&axis3DScaleFlag);
    Select(ID_axis3DScales,        (void *)axis3DScales, 3);
    Select(ID_shear,               (void *)shear, 3);
    Select(ID_windowValid,         (void *)&windowValid);
}

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

// ****************************************************************************
// Method: View3DAttributes::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
View3DAttributes::CreateNode(DataNode *parentNode, bool completeSave, bool forceAdd)
{
    if(parentNode == 0)
        return false;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

    return (addToParent || forceAdd);
}

// ****************************************************************************
// Method: View3DAttributes::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
View3DAttributes::SetFromNode(DataNode *parentNode)
{
    if(parentNode == 0)
        return;

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

    DataNode *node;
    if((node = searchNode->GetNode("viewNormal")) != 0)
        SetViewNormal(node->AsDoubleArray());
    if((node = searchNode->GetNode("focus")) != 0)
        SetFocus(node->AsDoubleArray());
    if((node = searchNode->GetNode("viewUp")) != 0)
        SetViewUp(node->AsDoubleArray());
    if((node = searchNode->GetNode("viewAngle")) != 0)
        SetViewAngle(node->AsDouble());
    if((node = searchNode->GetNode("parallelScale")) != 0)
        SetParallelScale(node->AsDouble());
    if((node = searchNode->GetNode("nearPlane")) != 0)
        SetNearPlane(node->AsDouble());
    if((node = searchNode->GetNode("farPlane")) != 0)
        SetFarPlane(node->AsDouble());
    if((node = searchNode->GetNode("imagePan")) != 0)
        SetImagePan(node->AsDoubleArray());
    if((node = searchNode->GetNode("imageZoom")) != 0)
        SetImageZoom(node->AsDouble());
    if((node = searchNode->GetNode("perspective")) != 0)
        SetPerspective(node->AsBool());
    if((node = searchNode->GetNode("eyeAngle")) != 0)
        SetEyeAngle(node->AsDouble());
    if((node = searchNode->GetNode("centerOfRotationSet")) != 0)
        SetCenterOfRotationSet(node->AsBool());
    if((node = searchNode->GetNode("centerOfRotation")) != 0)
        SetCenterOfRotation(node->AsDoubleArray());
    if((node = searchNode->GetNode("axis3DScaleFlag")) != 0)
        SetAxis3DScaleFlag(node->AsBool());
    if((node = searchNode->GetNode("axis3DScales")) != 0)
        SetAxis3DScales(node->AsDoubleArray());
    if((node = searchNode->GetNode("shear")) != 0)
        SetShear(node->AsDoubleArray());
    if((node = searchNode->GetNode("windowValid")) != 0)
        SetWindowValid(node->AsBool());
}

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

void
View3DAttributes::SetViewNormal(const double *viewNormal_)
{
    viewNormal[0] = viewNormal_[0];
    viewNormal[1] = viewNormal_[1];
    viewNormal[2] = viewNormal_[2];
    Select(ID_viewNormal, (void *)viewNormal, 3);
}

void
View3DAttributes::SetFocus(const double *focus_)
{
    focus[0] = focus_[0];
    focus[1] = focus_[1];
    focus[2] = focus_[2];
    Select(ID_focus, (void *)focus, 3);
}

void
View3DAttributes::SetViewUp(const double *viewUp_)
{
    viewUp[0] = viewUp_[0];
    viewUp[1] = viewUp_[1];
    viewUp[2] = viewUp_[2];
    Select(ID_viewUp, (void *)viewUp, 3);
}

void
View3DAttributes::SetViewAngle(double viewAngle_)
{
    viewAngle = viewAngle_;
    Select(ID_viewAngle, (void *)&viewAngle);
}

void
View3DAttributes::SetParallelScale(double parallelScale_)
{
    parallelScale = parallelScale_;
    Select(ID_parallelScale, (void *)&parallelScale);
}

void
View3DAttributes::SetNearPlane(double nearPlane_)
{
    nearPlane = nearPlane_;
    Select(ID_nearPlane, (void *)&nearPlane);
}

void
View3DAttributes::SetFarPlane(double farPlane_)
{
    farPlane = farPlane_;
    Select(ID_farPlane, (void *)&farPlane);
}

void
View3DAttributes::SetImagePan(const double *imagePan_)
{
    imagePan[0] = imagePan_[0];
    imagePan[1] = imagePan_[1];
    Select(ID_imagePan, (void *)imagePan, 2);
}

void
View3DAttributes::SetImageZoom(double imageZoom_)
{
    imageZoom = imageZoom_;
    Select(ID_imageZoom, (void *)&imageZoom);
}

void
View3DAttributes::SetPerspective(bool perspective_)
{
    perspective = perspective_;
    Select(ID_perspective, (void *)&perspective);
}

void
View3DAttributes::SetEyeAngle(double eyeAngle_)
{
    eyeAngle = eyeAngle_;
    Select(ID_eyeAngle, (void *)&eyeAngle);
}

void
View3DAttributes::SetCenterOfRotationSet(bool centerOfRotationSet_)
{
    centerOfRotationSet = centerOfRotationSet_;
    Select(ID_centerOfRotationSet, (void *)&centerOfRotationSet);
}

void
View3DAttributes::SetCenterOfRotation(const double *centerOfRotation_)
{
    centerOfRotation[0] = centerOfRotation_[0];
    centerOfRotation[1] = centerOfRotation_[1];
    centerOfRotation[2] = centerOfRotation_[2];
    Select(ID_centerOfRotation, (void *)centerOfRotation, 3);
}

void
View3DAttributes::SetAxis3DScaleFlag(bool axis3DScaleFlag_)
{
    axis3DScaleFlag = axis3DScaleFlag_;
    Select(ID_axis3DScaleFlag, (void *)&axis3DScaleFlag);
}

void
View3DAttributes::SetAxis3DScales(const double *axis3DScales_)
{
    axis3DScales[0] = axis3DScales_[0];
    axis3DScales[1] = axis3DScales_[1];
    axis3DScales[2] = axis3DScales_[2];
    Select(ID_axis3DScales, (void *)axis3DScales, 3);
}

void
View3DAttributes::SetShear(const double *shear_)
{
    shear[0] = shear_[0];
    shear[1] = shear_[1];
    shear[2] = shear_[2];
    Select(ID_shear, (void *)shear, 3);
}

void
View3DAttributes::SetWindowValid(bool windowValid_)
{
    windowValid = windowValid_;
    Select(ID_windowValid, (void *)&windowValid);
}

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

const double *
View3DAttributes::GetViewNormal() const
{
    return viewNormal;
}

double *
View3DAttributes::GetViewNormal()
{
    return viewNormal;
}

const double *
View3DAttributes::GetFocus() const
{
    return focus;
}

double *
View3DAttributes::GetFocus()
{
    return focus;
}

const double *
View3DAttributes::GetViewUp() const
{
    return viewUp;
}

double *
View3DAttributes::GetViewUp()
{
    return viewUp;
}

double
View3DAttributes::GetViewAngle() const
{
    return viewAngle;
}

double
View3DAttributes::GetParallelScale() const
{
    return parallelScale;
}

double
View3DAttributes::GetNearPlane() const
{
    return nearPlane;
}

double
View3DAttributes::GetFarPlane() const
{
    return farPlane;
}

const double *
View3DAttributes::GetImagePan() const
{
    return imagePan;
}

double *
View3DAttributes::GetImagePan()
{
    return imagePan;
}

double
View3DAttributes::GetImageZoom() const
{
    return imageZoom;
}

bool
View3DAttributes::GetPerspective() const
{
    return perspective;
}

double
View3DAttributes::GetEyeAngle() const
{
    return eyeAngle;
}

bool
View3DAttributes::GetCenterOfRotationSet() const
{
    return centerOfRotationSet;
}

const double *
View3DAttributes::GetCenterOfRotation() const
{
    return centerOfRotation;
}

double *
View3DAttributes::GetCenterOfRotation()
{
    return centerOfRotation;
}

bool
View3DAttributes::GetAxis3DScaleFlag() const
{
    return axis3DScaleFlag;
}

const double *
View3DAttributes::GetAxis3DScales() const
{
    return axis3DScales;
}

double *
View3DAttributes::GetAxis3DScales()
{
    return axis3DScales;
}

const double *
View3DAttributes::GetShear() const
{
    return shear;
}

double *
View3DAttributes::GetShear()
{
    return shear;
}

bool
View3DAttributes::GetWindowValid() const
{
    return windowValid;
}

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

void
View3DAttributes::SelectViewNormal()
{
    Select(ID_viewNormal, (void *)viewNormal, 3);
}

void
View3DAttributes::SelectFocus()
{
    Select(ID_focus, (void *)focus, 3);
}

void
View3DAttributes::SelectViewUp()
{
    Select(ID_viewUp, (void *)viewUp, 3);
}

void
View3DAttributes::SelectImagePan()
{
    Select(ID_imagePan, (void *)imagePan, 2);
}

void
View3DAttributes::SelectCenterOfRotation()
{
    Select(ID_centerOfRotation, (void *)centerOfRotation, 3);
}

void
View3DAttributes::SelectAxis3DScales()
{
    Select(ID_axis3DScales, (void *)axis3DScales, 3);
}

void
View3DAttributes::SelectShear()
{
    Select(ID_shear, (void *)shear, 3);
}

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

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

std::string
View3DAttributes::GetFieldName(int index) const
{
    switch (index)
    {
    case ID_viewNormal:          return "viewNormal";
    case ID_focus:               return "focus";
    case ID_viewUp:              return "viewUp";
    case ID_viewAngle:           return "viewAngle";
    case ID_parallelScale:       return "parallelScale";
    case ID_nearPlane:           return "nearPlane";
    case ID_farPlane:            return "farPlane";
    case ID_imagePan:            return "imagePan";
    case ID_imageZoom:           return "imageZoom";
    case ID_perspective:         return "perspective";
    case ID_eyeAngle:            return "eyeAngle";
    case ID_centerOfRotationSet: return "centerOfRotationSet";
    case ID_centerOfRotation:    return "centerOfRotation";
    case ID_axis3DScaleFlag:     return "axis3DScaleFlag";
    case ID_axis3DScales:        return "axis3DScales";
    case ID_shear:               return "shear";
    case ID_windowValid:         return "windowValid";
    default:  return "invalid index";
    }
}

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

AttributeGroup::FieldType
View3DAttributes::GetFieldType(int index) const
{
    switch (index)
    {
    case ID_viewNormal:          return FieldType_doubleArray;
    case ID_focus:               return FieldType_doubleArray;
    case ID_viewUp:              return FieldType_doubleArray;
    case ID_viewAngle:           return FieldType_double;
    case ID_parallelScale:       return FieldType_double;
    case ID_nearPlane:           return FieldType_double;
    case ID_farPlane:            return FieldType_double;
    case ID_imagePan:            return FieldType_doubleArray;
    case ID_imageZoom:           return FieldType_double;
    case ID_perspective:         return FieldType_bool;
    case ID_eyeAngle:            return FieldType_double;
    case ID_centerOfRotationSet: return FieldType_bool;
    case ID_centerOfRotation:    return FieldType_doubleArray;
    case ID_axis3DScaleFlag:     return FieldType_bool;
    case ID_axis3DScales:        return FieldType_doubleArray;
    case ID_shear:               return FieldType_doubleArray;
    case ID_windowValid:         return FieldType_bool;
    default:  return FieldType_unknown;
    }
}

// ****************************************************************************
// Method: View3DAttributes::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
View3DAttributes::GetFieldTypeName(int index) const
{
    switch (index)
    {
    case ID_viewNormal:          return "doubleArray";
    case ID_focus:               return "doubleArray";
    case ID_viewUp:              return "doubleArray";
    case ID_viewAngle:           return "double";
    case ID_parallelScale:       return "double";
    case ID_nearPlane:           return "double";
    case ID_farPlane:            return "double";
    case ID_imagePan:            return "doubleArray";
    case ID_imageZoom:           return "double";
    case ID_perspective:         return "bool";
    case ID_eyeAngle:            return "double";
    case ID_centerOfRotationSet: return "bool";
    case ID_centerOfRotation:    return "doubleArray";
    case ID_axis3DScaleFlag:     return "bool";
    case ID_axis3DScales:        return "doubleArray";
    case ID_shear:               return "doubleArray";
    case ID_windowValid:         return "bool";
    default:  return "invalid index";
    }
}

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

bool
View3DAttributes::FieldsEqual(int index_, const AttributeGroup *rhs) const
{
    const View3DAttributes &obj = *((const View3DAttributes*)rhs);
    bool retval = false;
    switch (index_)
    {
    case ID_viewNormal:
        {  // new scope
        // Compare the viewNormal arrays.
        bool viewNormal_equal = true;
        for(int i = 0; i < 3 && viewNormal_equal; ++i)
            viewNormal_equal = (viewNormal[i] == obj.viewNormal[i]);

        retval = viewNormal_equal;
        }
        break;
    case ID_focus:
        {  // new scope
        // Compare the focus arrays.
        bool focus_equal = true;
        for(int i = 0; i < 3 && focus_equal; ++i)
            focus_equal = (focus[i] == obj.focus[i]);

        retval = focus_equal;
        }
        break;
    case ID_viewUp:
        {  // new scope
        // Compare the viewUp arrays.
        bool viewUp_equal = true;
        for(int i = 0; i < 3 && viewUp_equal; ++i)
            viewUp_equal = (viewUp[i] == obj.viewUp[i]);

        retval = viewUp_equal;
        }
        break;
    case ID_viewAngle:
        {  // new scope
        retval = (viewAngle == obj.viewAngle);
        }
        break;
    case ID_parallelScale:
        {  // new scope
        retval = (parallelScale == obj.parallelScale);
        }
        break;
    case ID_nearPlane:
        {  // new scope
        retval = (nearPlane == obj.nearPlane);
        }
        break;
    case ID_farPlane:
        {  // new scope
        retval = (farPlane == obj.farPlane);
        }
        break;
    case ID_imagePan:
        {  // new scope
        // Compare the imagePan arrays.
        bool imagePan_equal = true;
        for(int i = 0; i < 2 && imagePan_equal; ++i)
            imagePan_equal = (imagePan[i] == obj.imagePan[i]);

        retval = imagePan_equal;
        }
        break;
    case ID_imageZoom:
        {  // new scope
        retval = (imageZoom == obj.imageZoom);
        }
        break;
    case ID_perspective:
        {  // new scope
        retval = (perspective == obj.perspective);
        }
        break;
    case ID_eyeAngle:
        {  // new scope
        retval = (eyeAngle == obj.eyeAngle);
        }
        break;
    case ID_centerOfRotationSet:
        {  // new scope
        retval = (centerOfRotationSet == obj.centerOfRotationSet);
        }
        break;
    case ID_centerOfRotation:
        {  // new scope
        // Compare the centerOfRotation arrays.
        bool centerOfRotation_equal = true;
        for(int i = 0; i < 3 && centerOfRotation_equal; ++i)
            centerOfRotation_equal = (centerOfRotation[i] == obj.centerOfRotation[i]);

        retval = centerOfRotation_equal;
        }
        break;
    case ID_axis3DScaleFlag:
        {  // new scope
        retval = (axis3DScaleFlag == obj.axis3DScaleFlag);
        }
        break;
    case ID_axis3DScales:
        {  // new scope
        // Compare the axis3DScales arrays.
        bool axis3DScales_equal = true;
        for(int i = 0; i < 3 && axis3DScales_equal; ++i)
            axis3DScales_equal = (axis3DScales[i] == obj.axis3DScales[i]);

        retval = axis3DScales_equal;
        }
        break;
    case ID_shear:
        {  // new scope
        // Compare the shear arrays.
        bool shear_equal = true;
        for(int i = 0; i < 3 && shear_equal; ++i)
            shear_equal = (shear[i] == obj.shear[i]);

        retval = shear_equal;
        }
        break;
    case ID_windowValid:
        {  // new scope
        retval = (windowValid == obj.windowValid);
        }
        break;
    default: retval = false;
    }

    return retval;
}

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

// **************************************************************************
// Function: RotateAxis
//
// Purpose: Modify the view by rotating it a given number of degrees about
//          the given axis
//
// Programmer: Mark Miller (orig. by Eric Brugger in QvisViewWindow.C)
// Creation:   May 15, 2008
// **************************************************************************

void View3DAttributes::RotateAxis(int axis, double angle)
{
    double angleRadians;
    double v1[3], v2[3], v3[3];
    double t1[16], t2[16], m1[16], m2[16], r[16];
    double ma[16], mb[16], mc[16];
    double rM[16];
    double viewNormal_tmp[3];
    double viewUp_tmp[3];
    double viewFocus_tmp[3];
    double dist;

    for(int i = 0; i < 15; ++i)
        r[i] = 0.0;

    //
    // Calculate the rotation matrix in screen coordinates.
    //
    angleRadians = angle * (3.141592653589793 / 180.);
    switch (axis)
    {
      case 0:
        r[0]  = 1.;
        r[1]  = 0.;
        r[2]  = 0.;
        r[3]  = 0.;
        r[4]  = 0.;
        r[5]  = cos(angleRadians);
        r[6]  = - sin(angleRadians);
        r[7]  = 0.;
        r[8]  = 0.;
        r[9]  = sin(angleRadians);
        r[10] = cos(angleRadians);
        r[11] = 0.;
        r[12] = 0.;
        r[13] = 0.;
        r[14] = 0.;
        r[15] = 1.;
        break;

      case 1:
        r[0]  = cos(angleRadians);
        r[1]  = 0.;
        r[2]  = sin(angleRadians);
        r[3]  = 0.;
        r[4]  = 0.;
        r[5]  = 1.;
        r[6]  = 0.;
        r[7]  = 0.;
        r[8]  = - sin(angleRadians);
        r[9]  = 0.;
        r[10]  = cos(angleRadians);
        r[11] = 0.;
        r[12] = 0.;
        r[13] = 0.;
        r[14] = 0.;
        r[15] = 1.;
        break;

      case 2:
        r[0]  = cos(angleRadians);
        r[1]  = - sin(angleRadians);
        r[2]  = 0.;
        r[3]  = 0.;
        r[4]  = sin(angleRadians);
        r[5]  = cos(angleRadians);
        r[6]  = 0.;
        r[7]  = 0.;
        r[8]  = 0.;
        r[9]  = 0.;
        r[10]  = 1.;
        r[11] = 0.;
        r[12] = 0.;
        r[13] = 0.;
        r[14] = 0.;
        r[15] = 1.;
        break;
    }

    //
    // Calculate the matrix to rotate from object coordinates to screen
    // coordinates and its inverse.
    //
    v1[0] = GetViewNormal()[0];
    v1[1] = GetViewNormal()[1];
    v1[2] = GetViewNormal()[2];

    v2[0] = GetViewUp()[0];
    v2[1] = GetViewUp()[1];
    v2[2] = GetViewUp()[2];

    v3[0] =   v2[1]*v1[2] - v2[2]*v1[1];
    v3[1] = - v2[0]*v1[2] + v2[2]*v1[0];
    v3[2] =   v2[0]*v1[1] - v2[1]*v1[0];

    m1[0]  = v3[0];
    m1[1]  = v2[0];
    m1[2]  = v1[0];
    m1[3]  = 0.;
    m1[4]  = v3[1];
    m1[5]  = v2[1];
    m1[6]  = v1[1];
    m1[7]  = 0.;
    m1[8]  = v3[2];
    m1[9]  = v2[2];
    m1[10] = v1[2];
    m1[11] = 0.;
    m1[12] = 0.;
    m1[13] = 0.;
    m1[14] = 0.;
    m1[15] = 1.;
    m2[0]  = m1[0];
    m2[1]  = m1[4];
    m2[2]  = m1[8];
    m2[3]  = m1[12];
    m2[4]  = m1[1];
    m2[5]  = m1[5];
    m2[6]  = m1[9];
    m2[7]  = m1[13];
    m2[8]  = m1[2];
    m2[9]  = m1[6];
    m2[10] = m1[10];
    m2[11] = m1[14];
    m2[12] = m1[3];
    m2[13] = m1[7];
    m2[14] = m1[11];
    m2[15] = m1[15];

    //
    // Calculate the translation to the center of rotation (and its
    // inverse).
    //
    t1[0]  = 1.;
    t1[1]  = 0.;
    t1[2]  = 0.;
    t1[3]  = 0.;
    t1[4]  = 0.;
    t1[5]  = 1.;
    t1[6]  = 0.;
    t1[7]  = 0.;
    t1[8]  = 0.;
    t1[9]  = 0.;
    t1[10] = 1.;
    t1[11] = 0.;
    t1[12] = -GetCenterOfRotation()[0];
    t1[13] = -GetCenterOfRotation()[1];
    t1[14] = -GetCenterOfRotation()[2];
    t1[15] = 1.;

    t2[0]  = 1.;
    t2[1]  = 0.;
    t2[2]  = 0.;
    t2[3]  = 0.;
    t2[4]  = 0.;
    t2[5]  = 1.;
    t2[6]  = 0.;
    t2[7]  = 0.;
    t2[8]  = 0.;
    t2[9]  = 0.;
    t2[10] = 1.;
    t2[11] = 0.;
    t2[12] = GetCenterOfRotation()[0];
    t2[13] = GetCenterOfRotation()[1];
    t2[14] = GetCenterOfRotation()[2];
    t2[14] = GetCenterOfRotation()[2];
    t2[15] = 1.;

    //
    // Form the composite transformation matrix t1 X m1 X r X m2 X t2.
    //
    ma[0]  = t1[0]*m1[0]  + t1[1]*m1[4]  + t1[2]*m1[8]   + t1[3]*m1[12];
    ma[1]  = t1[0]*m1[1]  + t1[1]*m1[5]  + t1[2]*m1[9]   + t1[3]*m1[13];
    ma[2]  = t1[0]*m1[2]  + t1[1]*m1[6]  + t1[2]*m1[10]  + t1[3]*m1[14];
    ma[3]  = t1[0]*m1[3]  + t1[1]*m1[7]  + t1[2]*m1[11]  + t1[3]*m1[15];
    ma[4]  = t1[4]*m1[0]  + t1[5]*m1[4]  + t1[6]*m1[8]   + t1[7]*m1[12];
    ma[5]  = t1[4]*m1[1]  + t1[5]*m1[5]  + t1[6]*m1[9]   + t1[7]*m1[13];
    ma[6]  = t1[4]*m1[2]  + t1[5]*m1[6]  + t1[6]*m1[10]  + t1[7]*m1[14];
    ma[7]  = t1[4]*m1[3]  + t1[5]*m1[7]  + t1[6]*m1[11]  + t1[7]*m1[15];
    ma[8]  = t1[8]*m1[0]  + t1[9]*m1[4]  + t1[10]*m1[8]  + t1[11]*m1[12];
    ma[9]  = t1[8]*m1[1]  + t1[9]*m1[5]  + t1[10]*m1[9]  + t1[11]*m1[13];
    ma[10] = t1[8]*m1[2]  + t1[9]*m1[6]  + t1[10]*m1[10] + t1[11]*m1[14];
    ma[11] = t1[8]*m1[3]  + t1[9]*m1[7]  + t1[10]*m1[11] + t1[11]*m1[15];
    ma[12] = t1[12]*m1[0] + t1[13]*m1[4] + t1[14]*m1[8]  + t1[15]*m1[12];
    ma[13] = t1[12]*m1[1] + t1[13]*m1[5] + t1[14]*m1[9]  + t1[15]*m1[13];
    ma[14] = t1[12]*m1[2] + t1[13]*m1[6] + t1[14]*m1[10] + t1[15]*m1[14];
    ma[15] = t1[12]*m1[3] + t1[13]*m1[7] + t1[14]*m1[11] + t1[15]*m1[15];

    mb[0]  = ma[0]*r[0]  + ma[1]*r[4]  + ma[2]*r[8]   + ma[3]*r[12];
    mb[1]  = ma[0]*r[1]  + ma[1]*r[5]  + ma[2]*r[9]   + ma[3]*r[13];
    mb[2]  = ma[0]*r[2]  + ma[1]*r[6]  + ma[2]*r[10]  + ma[3]*r[14];
    mb[3]  = ma[0]*r[3]  + ma[1]*r[7]  + ma[2]*r[11]  + ma[3]*r[15];
    mb[4]  = ma[4]*r[0]  + ma[5]*r[4]  + ma[6]*r[8]   + ma[7]*r[12];
    mb[5]  = ma[4]*r[1]  + ma[5]*r[5]  + ma[6]*r[9]   + ma[7]*r[13];
    mb[6]  = ma[4]*r[2]  + ma[5]*r[6]  + ma[6]*r[10]  + ma[7]*r[14];
    mb[7]  = ma[4]*r[3]  + ma[5]*r[7]  + ma[6]*r[11]  + ma[7]*r[15];
    mb[8]  = ma[8]*r[0]  + ma[9]*r[4]  + ma[10]*r[8]  + ma[11]*r[12];
    mb[9]  = ma[8]*r[1]  + ma[9]*r[5]  + ma[10]*r[9]  + ma[11]*r[13];
    mb[10] = ma[8]*r[2]  + ma[9]*r[6]  + ma[10]*r[10] + ma[11]*r[14];
    mb[11] = ma[8]*r[3]  + ma[9]*r[7]  + ma[10]*r[11] + ma[11]*r[15];
    mb[12] = ma[12]*r[0] + ma[13]*r[4] + ma[14]*r[8]  + ma[15]*r[12];
    mb[13] = ma[12]*r[1] + ma[13]*r[5] + ma[14]*r[9]  + ma[15]*r[13];
    mb[14] = ma[12]*r[2] + ma[13]*r[6] + ma[14]*r[10] + ma[15]*r[14];
    mb[15] = ma[12]*r[3] + ma[13]*r[7] + ma[14]*r[11] + ma[15]*r[15];

    mc[0]  = mb[0]*m2[0]  + mb[1]*m2[4]  + mb[2]*m2[8]   + mb[3]*m2[12];
    mc[1]  = mb[0]*m2[1]  + mb[1]*m2[5]  + mb[2]*m2[9]   + mb[3]*m2[13];
    mc[2]  = mb[0]*m2[2]  + mb[1]*m2[6]  + mb[2]*m2[10]  + mb[3]*m2[14];
    mc[3]  = mb[0]*m2[3]  + mb[1]*m2[7]  + mb[2]*m2[11]  + mb[3]*m2[15];
    mc[4]  = mb[4]*m2[0]  + mb[5]*m2[4]  + mb[6]*m2[8]   + mb[7]*m2[12];
    mc[5]  = mb[4]*m2[1]  + mb[5]*m2[5]  + mb[6]*m2[9]   + mb[7]*m2[13];
    mc[6]  = mb[4]*m2[2]  + mb[5]*m2[6]  + mb[6]*m2[10]  + mb[7]*m2[14];
    mc[7]  = mb[4]*m2[3]  + mb[5]*m2[7]  + mb[6]*m2[11]  + mb[7]*m2[15];
    mc[8]  = mb[8]*m2[0]  + mb[9]*m2[4]  + mb[10]*m2[8]  + mb[11]*m2[12];
    mc[9]  = mb[8]*m2[1]  + mb[9]*m2[5]  + mb[10]*m2[9]  + mb[11]*m2[13];
    mc[10] = mb[8]*m2[2]  + mb[9]*m2[6]  + mb[10]*m2[10] + mb[11]*m2[14];
    mc[11] = mb[8]*m2[3]  + mb[9]*m2[7]  + mb[10]*m2[11] + mb[11]*m2[15];
    mc[12] = mb[12]*m2[0] + mb[13]*m2[4] + mb[14]*m2[8]  + mb[15]*m2[12];
    mc[13] = mb[12]*m2[1] + mb[13]*m2[5] + mb[14]*m2[9]  + mb[15]*m2[13];
    mc[14] = mb[12]*m2[2] + mb[13]*m2[6] + mb[14]*m2[10] + mb[15]*m2[14];
    mc[15] = mb[12]*m2[3] + mb[13]*m2[7] + mb[14]*m2[11] + mb[15]*m2[15];

    rM[0]  = mc[0]*t2[0]  + mc[1]*t2[4]  + mc[2]*t2[8]   + mc[3]*t2[12];
    rM[1]  = mc[0]*t2[1]  + mc[1]*t2[5]  + mc[2]*t2[9]   + mc[3]*t2[13];
    rM[2]  = mc[0]*t2[2]  + mc[1]*t2[6]  + mc[2]*t2[10]  + mc[3]*t2[14];
    rM[3]  = mc[0]*t2[3]  + mc[1]*t2[7]  + mc[2]*t2[11]  + mc[3]*t2[15];
    rM[4]  = mc[4]*t2[0]  + mc[5]*t2[4]  + mc[6]*t2[8]   + mc[7]*t2[12];
    rM[5]  = mc[4]*t2[1]  + mc[5]*t2[5]  + mc[6]*t2[9]   + mc[7]*t2[13];
    rM[6]  = mc[4]*t2[2]  + mc[5]*t2[6]  + mc[6]*t2[10]  + mc[7]*t2[14];
    rM[7]  = mc[4]*t2[3]  + mc[5]*t2[7]  + mc[6]*t2[11]  + mc[7]*t2[15];
    rM[8]  = mc[8]*t2[0]  + mc[9]*t2[4]  + mc[10]*t2[8]  + mc[11]*t2[12];
    rM[9]  = mc[8]*t2[1]  + mc[9]*t2[5]  + mc[10]*t2[9]  + mc[11]*t2[13];
    rM[10] = mc[8]*t2[2]  + mc[9]*t2[6]  + mc[10]*t2[10] + mc[11]*t2[14];
    rM[11] = mc[8]*t2[3]  + mc[9]*t2[7]  + mc[10]*t2[11] + mc[11]*t2[15];
    rM[12] = mc[12]*t2[0] + mc[13]*t2[4] + mc[14]*t2[8]  + mc[15]*t2[12];
    rM[13] = mc[12]*t2[1] + mc[13]*t2[5] + mc[14]*t2[9]  + mc[15]*t2[13];
    rM[14] = mc[12]*t2[2] + mc[13]*t2[6] + mc[14]*t2[10] + mc[15]*t2[14];
    rM[15] = mc[12]*t2[3] + mc[13]*t2[7] + mc[14]*t2[11] + mc[15]*t2[15];

    //
    // Calculate the new view normal and view up.
    //
    viewNormal_tmp[0] = GetViewNormal()[0] * rM[0] +
                    GetViewNormal()[1] * rM[4] +
                    GetViewNormal()[2] * rM[8];
    viewNormal_tmp[1] = GetViewNormal()[0] * rM[1] +
                    GetViewNormal()[1] * rM[5] +
                    GetViewNormal()[2] * rM[9];
    viewNormal_tmp[2] = GetViewNormal()[0] * rM[2] +
                    GetViewNormal()[1] * rM[6] +
                    GetViewNormal()[2] * rM[10];
    dist = sqrt(viewNormal_tmp[0]*viewNormal_tmp[0] + viewNormal_tmp[1]*viewNormal_tmp[1] +
                viewNormal_tmp[2]*viewNormal_tmp[2]);
    viewNormal_tmp[0] /= dist;
    viewNormal_tmp[1] /= dist;
    viewNormal_tmp[2] /= dist;

    SetViewNormal(viewNormal_tmp);

    viewUp_tmp[0] = GetViewUp()[0] * rM[0] +
                GetViewUp()[1] * rM[4] +
                GetViewUp()[2] * rM[8];
    viewUp_tmp[1] = GetViewUp()[0] * rM[1] +
                GetViewUp()[1] * rM[5] +
                GetViewUp()[2] * rM[9];
    viewUp_tmp[2] = GetViewUp()[0] * rM[2] +
                GetViewUp()[1] * rM[6] +
                GetViewUp()[2] * rM[10];
    dist = sqrt(viewUp_tmp[0]*viewUp_tmp[0] + viewUp_tmp[1]*viewUp_tmp[1] +
                viewUp_tmp[2]*viewUp_tmp[2]);
    viewUp_tmp[0] /= dist;
    viewUp_tmp[1] /= dist;
    viewUp_tmp[2] /= dist;

    SetViewUp(viewUp_tmp);

    if (GetCenterOfRotationSet())
    {
        viewFocus_tmp[0] = GetFocus()[0] * rM[0]  +
                       GetFocus()[1] * rM[4]  +
                       GetFocus()[2] * rM[8]  +
                       rM[12];
        viewFocus_tmp[1] = GetFocus()[0] * rM[1]  +
                       GetFocus()[1] * rM[5]  +
                       GetFocus()[2] * rM[9]  +
                       rM[13];
        viewFocus_tmp[2] = GetFocus()[0] * rM[2]  +
                       GetFocus()[1] * rM[6]  +
                       GetFocus()[2] * rM[10] +
                       rM[14];

        SetFocus(viewFocus_tmp);
    }
}

#include <math.h>
// ****************************************************************************
// Method: View3DAttributes::ResetView
//
// Purpose: 
//   Resets the view to something that can accommodate the bbox.
//
// Arguments:
//   bbox : The 3D extents that we're trying to look at.
//
// Programmer: Brad Whitlock
// Creation:   Tue Mar  3 16:21:14 PST 2009
//
// Modifications:
//    Jeremy Meredith, Mon Aug  2 14:23:08 EDT 2010
//    Add shear for oblique projection support.
//   
// ****************************************************************************
void
View3DAttributes::ResetView(const double *bbox)
{
    View3DAttributes view3D;

    //
    // Set the scale.  They are choosen such that the object should fit
    // within a square window no matter the orientation when doing an
    // orthographic projection.  The parallelScale controls the scale
    // with orthographic projections, whereas the distance controls the
    // scale with perspective projections.
    //
    double width = 0.5 * sqrt(((bbox[1] - bbox[0]) *
                               (bbox[1] - bbox[0])) +
                              ((bbox[3] - bbox[2]) *
                               (bbox[3] - bbox[2])) +
                              ((bbox[5] - bbox[4]) *
                               (bbox[5] - bbox[4])));
    
    if( width == 0.0 )
      width = 0.001;

    view3D.parallelScale = width;
    view3D.viewAngle = 30.;

    //
    // Set the view up vector, the focal point and the camera position.
    //
    view3D.viewNormal[0] = 0.;
    view3D.viewNormal[1] = 0.;
    view3D.viewNormal[2] = 1.;

    view3D.focus[0] = (bbox[1] + bbox[0]) / 2.;
    view3D.focus[1] = (bbox[3] + bbox[2]) / 2.;
    view3D.focus[2] = (bbox[5] + bbox[4]) / 2.;

    view3D.viewUp[0] = 0.;
    view3D.viewUp[1] = 1.;
    view3D.viewUp[2] = 0.;

    //
    // Calculate the near and far clipping planes.  The clipping planes are
    // set such that the object should not get clipped in the front or
    // back no matter the orientation when doing an orthographic projection.
    //
    view3D.nearPlane = - 2.0 * width;
    view3D.farPlane  =   2.0 * width;

    //
    // Reset the image pan and image zoom.
    //
    view3D.imagePan[0] = 0.;
    view3D.imagePan[1] = 0.;
    view3D.imageZoom = 1.;

    //
    // Reset the center of rotation.
    //
    view3D.centerOfRotationSet = false;
    view3D.centerOfRotation[0] = view3D.focus[0];
    view3D.centerOfRotation[1] = view3D.focus[1];
    view3D.centerOfRotation[2] = view3D.focus[2];

    //
    // Reset the shear
    //
    view3D.shear[0] = 0.;
    view3D.shear[1] = 0.;
    view3D.shear[2] = 1.;

    // Copy the object into this.
    *this = view3D;
}
