Index: vtkvis/tags/1.8.4/Arc.cpp
===================================================================
--- vtkvis/tags/1.8.4/Arc.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Arc.cpp	(revision 6184)
@@ -0,0 +1,47 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkArcSource.h>
+
+#include "Arc.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Arc::Arc() :
+    Shape()
+{
+}
+
+Arc::~Arc()
+{
+    TRACE("Deleting Arc");
+}
+
+void Arc::update()
+{
+    if (_arc == NULL) {
+        _arc = vtkSmartPointer<vtkArcSource>::New();
+        _arc->SetResolution(4);
+        _arc->UseNormalAndAngleOn();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    _pdMapper->SetInputConnection(_arc->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
Index: vtkvis/tags/1.8.4/Arc.h
===================================================================
--- vtkvis/tags/1.8.4/Arc.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Arc.h	(revision 6184)
@@ -0,0 +1,91 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_ARC_H
+#define VTKVIS_ARC_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkArcSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData Arc
+ *
+ * This class creates a arc
+ */
+class Arc : public Shape
+{
+public:
+    Arc();
+    virtual ~Arc();
+
+    virtual const char *getClassName() const
+    {
+        return "Arc";
+    }
+
+    void setCenter(double center[3])
+    {
+        if (_arc != NULL) {
+            _arc->SetCenter(center);
+	}
+    }
+
+    void setStartPoint(double pt[3])
+    {
+        if (_arc != NULL) {
+            double polarVec[3];
+            for (int i = 0; i < 3; i++) {
+                polarVec[i] = pt[i] - _arc->GetCenter()[i];
+            }
+            setPolarVector(polarVec);
+ 	}
+    }
+
+    void setNormal(double norm[3])
+    {
+        if (_arc != NULL) {
+            _arc->SetNormal(norm);
+	}
+    }
+
+    void setPolarVector(double vec[3])
+    {
+        if (_arc != NULL) {
+            _arc->SetPolarVector(vec);
+	}
+    }
+
+    void setAngle(double angle)
+    {
+        if (_arc != NULL) {
+            _arc->SetAngle(angle);
+ 	}
+    }
+
+    void setResolution(int res)
+    {
+        if (_arc != NULL) {
+            _arc->SetResolution(res);
+	}
+    }
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkArcSource> _arc;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Arrow.cpp
===================================================================
--- vtkvis/tags/1.8.4/Arrow.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Arrow.cpp	(revision 6184)
@@ -0,0 +1,68 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkArrowSource.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkReverseSense.h>
+
+#include "Arrow.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Arrow::Arrow() :
+    Shape()
+{
+}
+
+Arrow::~Arrow()
+{
+    TRACE("Deleting Arrow");
+}
+
+void Arrow::update()
+{
+    if (_arrow == NULL) {
+	_arrow = vtkSmartPointer<vtkArrowSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+    normalFilter->SetFeatureAngle(90.);
+    normalFilter->SetInputConnection(_arrow->GetOutputPort());
+
+    _pdMapper->SetInputConnection(_arrow->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+void Arrow::flipNormals(bool state)
+{
+    if (_arrow == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_arrow->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_arrow->GetOutputPort());
+    }
+}
Index: vtkvis/tags/1.8.4/Arrow.h
===================================================================
--- vtkvis/tags/1.8.4/Arrow.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Arrow.h	(revision 6184)
@@ -0,0 +1,80 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_ARROW_H
+#define VTKVIS_ARROW_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkArrowSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData 3D Arrow
+ *
+ * This class creates a mesh arrow
+ */
+class Arrow : public Shape
+{
+public:
+    Arrow();
+    virtual ~Arrow();
+
+    virtual const char *getClassName() const
+    {
+        return "Arrow";
+    }
+
+    void setTipLength(double len)
+    {
+        if (_arrow != NULL) {
+            _arrow->SetTipLength(len);
+	}
+    }
+
+    void setRadii(double tipRadius, double shaftRadius)
+    {
+        if (_arrow != NULL) {
+            _arrow->SetTipRadius(tipRadius);
+            _arrow->SetShaftRadius(shaftRadius);
+	}
+    }
+
+    void setResolution(int tipRes, int shaftRes)
+    {
+        if (_arrow != NULL) {
+            _arrow->SetTipResolution(tipRes);
+            _arrow->SetShaftResolution(shaftRes);
+	}
+    }
+
+    /**
+     * \brief Invert head/tail of arrow
+     */
+    void setInvert(bool state)
+    {
+        if (_arrow != NULL) {
+            _arrow->SetInvert(state);
+	}
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkArrowSource> _arrow;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Box.cpp
===================================================================
--- vtkvis/tags/1.8.4/Box.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Box.cpp	(revision 6184)
@@ -0,0 +1,69 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkPolyData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkTransform.h>
+#include <vtkCubeSource.h>
+#include <vtkReverseSense.h>
+
+#include "Box.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Box::Box() :
+    Shape()
+{
+}
+
+Box::~Box()
+{
+    TRACE("Deleting Box");
+}
+
+void Box::update()
+{
+    if (_box == NULL) {
+	_box = vtkSmartPointer<vtkCubeSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    _pdMapper->SetInputConnection(_box->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+void Box::flipNormals(bool state)
+{
+    if (_box == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_box->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_box->GetOutputPort());
+    }
+}
Index: vtkvis/tags/1.8.4/Box.h
===================================================================
--- vtkvis/tags/1.8.4/Box.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Box.h	(revision 6184)
@@ -0,0 +1,56 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_BOX_H
+#define VTKVIS_BOX_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkCubeSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData box
+ *
+ * This class creates a mesh box
+ */
+class Box : public Shape
+{
+public:
+    Box();
+    virtual ~Box();
+
+    virtual const char *getClassName() const
+    {
+        return "Box";
+    }
+
+    void setSize(double xlen, double ylen, double zlen)
+    {
+        if (_box != NULL) {
+            _box->SetXLength(xlen);
+	    _box->SetYLength(ylen);
+	    _box->SetZLength(zlen);
+	}
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkCubeSource> _box;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/CmdProc.cpp
===================================================================
--- vtkvis/tags/1.8.4/CmdProc.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/CmdProc.cpp	(revision 6184)
@@ -0,0 +1,213 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#include <cstring>
+#include <tcl.h>
+
+#include "CmdProc.h"
+
+using namespace VtkVis;
+
+/**
+ * Performs a linear search on the array of command operation
+ * specifications to find a partial, anchored match for the given
+ * operation string.
+ *
+ * \return If the string matches unambiguously the index of the specification in
+ * the array is returned.  If the string does not match, even as an
+ * abbreviation, any operation, -1 is returned.  If the string matches,
+ * but ambiguously -2 is returned.
+ */
+static int
+LinearOpSearch(CmdSpec *specs, int low, int high, const char *string,
+               int length)
+{
+    CmdSpec *specPtr;
+    char c;
+    int nMatches, last;
+    int i;
+
+    c = string[0];
+    nMatches = 0;
+    last = -1;
+    for (specPtr = specs+low, i = low; i <= high; i++, specPtr++) {
+        if ((c == specPtr->name[0]) && 
+            (strncmp(string, specPtr->name, length) == 0)) {
+            last = i;
+            nMatches++;
+            if (length == specPtr->minChars) {
+                break;
+            }
+        }
+    }
+    if (nMatches > 1) {
+        return -2;      /* Ambiguous operation name */
+    } 
+    if (nMatches == 0) {
+        return -1;      /* Can't find operation */
+    } 
+    return last;        /* Op found. */
+}
+
+/**
+ * Performs a binary search on the array of command operation
+ * specifications to find a partial, anchored match for the given
+ * operation string.  If we get to a point where the sample string
+ * partially matches an entry, we then revert to a linear search
+ * over the given range to check if it's an exact match or
+ * ambiguous (example: "color" with commands color and colormode).
+ *
+ * \return If the string matches unambiguously the index of the specification in
+ * the array is returned.  If the string does not match, even as an
+ * abbreviation, any operation, -1 is returned.  If the string matches,
+ * but ambiguously -2 is returned.
+ */
+static int
+BinaryOpSearch(CmdSpec *specs, int low, int high, const char *string,
+               int length)
+{
+    char c;
+
+    c = string[0];
+    while (low <= high) {
+        CmdSpec *specPtr;
+        int compare;
+        int median;
+    
+        median = (low + high) >> 1;
+        specPtr = specs + median;
+
+        /* Test the first character */
+        compare = c - specPtr->name[0];
+        if (compare == 0) {
+            /* Now test the entire string */
+            compare = strncmp(string, specPtr->name, length);
+        }
+        if (compare < 0) {
+            high = median - 1;
+        } else if (compare > 0) {
+            low = median + 1;
+        } else {
+            if (length < specPtr->minChars) {
+                /* Verify that the string is either ambiguous or an exact
+                 * match of another command by doing a linear search over the
+                 * given interval. */
+                return LinearOpSearch(specs, low, high, string, length);
+            }
+            return median;  /* Op found. */
+        }
+    }
+    return -1;          /* Can't find operation */
+}
+
+/**
+ * \brief Find the command operation given a string name.
+ * 
+ * This is useful where a group of command operations have the same argument 
+ * signature.
+ *
+ * \return If found, a pointer to the procedure (function pointer) is returned.
+ * Otherwise NULL is returned and an error message containing a list of
+ * the possible commands is returned in interp->result.
+ */
+Tcl_ObjCmdProc *
+VtkVis::GetOpFromObj(Tcl_Interp *interp,	/* Interpreter to report errors to */
+                     int nSpecs,		/* Number of specifications in array */
+                     CmdSpec *specs,		/* Op specification array */
+                     int operPos,		/* Position of operation in argument
+                                                 * list. */
+                     int objc,			/* Number of arguments in the argument
+                                                 * vector.  This includes any prefixed
+                                                 * arguments */ 
+                     Tcl_Obj *const *objv,	/* Argument vector */
+                     int flags)
+{
+    CmdSpec *specPtr;
+    char *string;
+    int length;
+    int n;
+
+    if (objc <= operPos) {  /* No operation argument */
+        if (interp != NULL) {
+            Tcl_AppendResult(interp, "wrong # args: ", (char *)NULL);
+        usage:
+            Tcl_AppendResult(interp, "should be one of...", (char *)NULL);
+            for (n = 0; n < nSpecs; n++) {
+                int i;
+
+                Tcl_AppendResult(interp, "\n  ", (char *)NULL);
+                for (i = 0; i < operPos; i++) {
+                    Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ", 
+                                     (char *)NULL);
+                }
+                specPtr = specs + n;
+                Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage,
+                                 (char *)NULL);
+            }
+        }
+        return NULL;
+    }
+    string = Tcl_GetStringFromObj(objv[operPos], &length);
+    if (flags & CMDSPEC_LINEAR_SEARCH) {
+        n = LinearOpSearch(specs, 0, nSpecs - 1, string, length);
+    } else {
+        n = BinaryOpSearch(specs, 0, nSpecs - 1, string, length);
+    }
+    if (n == -2) {
+        if (interp != NULL) {
+            char c;
+
+            Tcl_AppendResult(interp, "ambiguous", (char *)NULL);
+            if (operPos > 2) {
+                Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), 
+                                 (char *)NULL);
+            }
+            Tcl_AppendResult(interp, " operation \"", string, "\" matches: ",
+                             (char *)NULL);
+
+            c = string[0];
+            length = strlen(string);
+            for (n = 0; n < nSpecs; n++) {
+                specPtr = specs + n;
+                if ((c == specPtr->name[0]) &&
+                    (strncmp(string, specPtr->name, length) == 0)) {
+                    Tcl_AppendResult(interp, " ", specPtr->name, (char *)NULL);
+                }
+            }
+        }
+        return NULL;
+
+    } else if (n == -1) {   /* Can't find operation, display help */
+        if (interp != NULL) {
+            Tcl_AppendResult(interp, "bad", (char *)NULL);
+            if (operPos > 2) {
+                Tcl_AppendResult(interp, " ", Tcl_GetString(objv[operPos - 1]), 
+                                 (char *)NULL);
+            }
+            Tcl_AppendResult(interp, " operation \"", string, "\": ", (char *)NULL);
+            goto usage;
+        }
+        return NULL;
+    }
+    specPtr = specs + n;
+    if ((objc < specPtr->minArgs) || 
+        ((specPtr->maxArgs > 0) && (objc > specPtr->maxArgs))) {
+        if (interp != NULL) {
+            int i;
+
+            Tcl_AppendResult(interp, "wrong # args: should be \"", (char *)NULL);
+            for (i = 0; i < operPos; i++) {
+                Tcl_AppendResult(interp, Tcl_GetString(objv[i]), " ", 
+                                 (char *)NULL);
+            }
+            Tcl_AppendResult(interp, specPtr->name, " ", specPtr->usage, "\"",
+                             (char *)NULL);
+        }
+        return NULL;
+    }
+    return specPtr->proc;
+}
Index: vtkvis/tags/1.8.4/CmdProc.h
===================================================================
--- vtkvis/tags/1.8.4/CmdProc.h	(revision 6184)
+++ vtkvis/tags/1.8.4/CmdProc.h	(revision 6184)
@@ -0,0 +1,49 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#ifndef VTKVIS_CMDPROC_H
+#define VTKVIS_CMDPROC_H
+
+#include <tcl.h>
+
+namespace VtkVis {
+
+/**
+ * \brief Structure to specify a set of operations for a Tcl command.
+ *
+ * This is passed to the GetOpFromObj procedure to look
+ * for a function pointer associated with the operation name.
+ */
+struct CmdSpec {
+    const char *name;		/**< Name of operation */
+    int minChars;		/**< Minimum # characters to disambiguate */
+    Tcl_ObjCmdProc *proc;
+    int minArgs;		/**< Minimum # args required */
+    int maxArgs;		/**< Maximum # args required */
+    const char *usage;		/**< Usage message */
+};
+
+enum CmdSpecIndex {
+    CMDSPEC_ARG0,		/**< Op is the first argument. */
+    CMDSPEC_ARG1,		/**< Op is the second argument. */
+    CMDSPEC_ARG2,		/**< Op is the third argument. */
+    CMDSPEC_ARG3,		/**< Op is the fourth argument. */
+    CMDSPEC_ARG4		/**< Op is the fifth argument. */
+};
+
+#define CMDSPEC_LINEAR_SEARCH	1
+#define CMDSPEC_BINARY_SEARCH	0
+
+extern Tcl_ObjCmdProc *
+GetOpFromObj(Tcl_Interp *interp, int nSpecs, 
+             CmdSpec *specs, int operPos, int objc, Tcl_Obj *const *objv, int flags);
+
+#define NumCmdSpecs(s) (sizeof(s) / sizeof(VtkVis::CmdSpec))
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/ColorMap.cpp
===================================================================
--- vtkvis/tags/1.8.4/ColorMap.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/ColorMap.cpp	(revision 6184)
@@ -0,0 +1,586 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <string>
+#include <list>
+#include <cstring>
+#include <cassert>
+
+#include <vtkLookupTable.h>
+#include <vtkColorTransferFunction.h>
+#include <vtkPiecewiseFunction.h>
+
+#include "ColorMap.h"
+#include "Molecule.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+ColorMap *ColorMap::_default = NULL;
+ColorMap *ColorMap::_grayDefault = NULL;
+ColorMap *ColorMap::_volumeDefault = NULL;
+ColorMap *ColorMap::_elementDefault = NULL;
+
+ColorMap::ColorMap(const std::string& name) :
+    _name(name),
+    _needsBuild(true),
+    _numTableEntries(256)
+{
+    _colorTF = vtkSmartPointer<vtkColorTransferFunction>::New();
+    _colorTF->ClampingOn();
+    _opacityTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
+    _opacityTF->ClampingOn();
+}
+
+ColorMap::ColorMap(const ColorMap& other) :
+    _name(other._name),
+    _controlPoints(other._controlPoints),
+    _opacityControlPoints(other._opacityControlPoints),
+    _needsBuild(other._needsBuild),
+    _numTableEntries(other._numTableEntries)
+{
+    _colorTF = vtkSmartPointer<vtkColorTransferFunction>::New();
+    _colorTF->ClampingOn();
+    _opacityTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
+    _opacityTF->ClampingOn();
+    _colorTF->DeepCopy(other._colorTF);
+    _opacityTF->DeepCopy(other._opacityTF);
+    if (other._lookupTable != NULL) {
+        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
+        _lookupTable->DeepCopy(other._lookupTable);
+    }
+}
+
+ColorMap& ColorMap::operator=(const ColorMap& other)
+{
+    if (&other != this) {
+        _name = other._name;
+        _controlPoints = other._controlPoints;
+        _opacityControlPoints = other._opacityControlPoints;
+        _needsBuild = other._needsBuild;
+        _numTableEntries = other._numTableEntries;
+        _colorTF->DeepCopy(other._colorTF);
+        _opacityTF->DeepCopy(other._opacityTF);
+        if (other._lookupTable != NULL) {
+            _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
+            _lookupTable->DeepCopy(other._lookupTable);
+        }
+    }
+    return *this;
+}
+
+ColorMap::~ColorMap()
+{
+}
+
+/**
+ * \brief Return the name/ID of the ColorMap
+ */
+const std::string& ColorMap::getName()
+{
+    return _name;
+}
+
+/**
+ * \brief Build and return the vtkLookupTable from the transfer function
+ */
+vtkLookupTable *ColorMap::getLookupTable()
+{
+    build();
+    assert(_lookupTable != NULL);
+    return _lookupTable;
+}
+
+/**
+ * \brief Return a newly allocated color transfer function with values
+ * scaled to the given data range
+ */
+vtkSmartPointer<vtkColorTransferFunction>
+ColorMap::getColorTransferFunction(double range[2])
+{
+    vtkSmartPointer<vtkColorTransferFunction> tf = vtkSmartPointer<vtkColorTransferFunction>::New();
+    double tmp[6];
+    for (int i = 0; i < _colorTF->GetSize(); i++) {
+        _colorTF->GetNodeValue(i, tmp);
+        //TRACE("Norm: %d: %g %g,%g,%g", i, tmp[0], tmp[1], tmp[2], tmp[3]);
+        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
+        tf->AddRGBPoint(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]);
+        //TRACE("New : %d: %g %g,%g,%g", i, tmp[0], tmp[1], tmp[2], tmp[3]);
+    }
+    return tf;
+}
+
+/**
+ * \brief Return a newly allocated opacity transfer function with values
+ * scaled to the given data range
+ */
+vtkSmartPointer<vtkPiecewiseFunction>
+ColorMap::getOpacityTransferFunction(double range[2], double opacityScale)
+{
+    vtkSmartPointer<vtkPiecewiseFunction> tf = vtkSmartPointer<vtkPiecewiseFunction>::New();
+    double tmp[4];
+    for (int i = 0; i < _opacityTF->GetSize(); i++) {
+        _opacityTF->GetNodeValue(i, tmp);
+        //TRACE("Norm: %d: %g %g", i, tmp[0], tmp[1]);
+        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
+        if (opacityScale < 1.0) {
+            tmp[1] *= opacityScale;
+        }
+        tf->AddPoint(tmp[0], tmp[1], tmp[2], tmp[3]);
+        //TRACE("New : %d: %g %g", i, tmp[0], tmp[1]);
+    }
+    return tf;
+}
+
+/**
+ * \brief Insert a new RGB control point into the transfer function
+ */
+void ColorMap::addControlPoint(ControlPoint& cp)
+{
+    _needsBuild = true;
+    // Clamp value,midpoint,sharpness to [0,1]
+    if (cp.value < 0.0)
+        cp.value = 0.0;
+    if (cp.value > 1.0)
+        cp.value = 1.0;
+    if (cp.midpoint < 0.0)
+        cp.midpoint = 0.0;
+    if (cp.midpoint > 1.0)
+        cp.midpoint = 1.0;
+    if (cp.sharpness < 0.0)
+        cp.sharpness = 0.0;
+    if (cp.sharpness > 1.0)
+        cp.sharpness = 1.0;
+
+#ifdef DEBUG
+    TRACE("New control point: %g = %g %g %g, %g %g",
+          cp.value, cp.color[0], cp.color[1], cp.color[2],
+          cp.midpoint, cp.sharpness);
+#endif
+    for (std::list<ControlPoint>::iterator itr = _controlPoints.begin();
+         itr != _controlPoints.end(); ++itr) {
+        if (itr->value == cp.value) {
+            *itr = cp;
+            return;
+        } else if (itr->value > cp.value) {
+            _controlPoints.insert(itr, cp);
+            return;
+        }
+    }
+    // If we reach here, our control point goes at the end
+    _controlPoints.insert(_controlPoints.end(), cp);
+    _colorTF->AddRGBPoint(cp.value, cp.color[0], cp.color[1], cp.color[2],
+                          cp.midpoint, cp.sharpness);
+}
+
+/**
+ * \brief Insert a new opacity/alpha control point into the transfer function
+ */
+void ColorMap::addOpacityControlPoint(OpacityControlPoint& cp)
+{
+    _needsBuild = true;
+    // Clamp value,midpoint,sharpness to [0,1]
+    if (cp.value < 0.0)
+        cp.value = 0.0;
+    if (cp.value > 1.0)
+        cp.value = 1.0;
+    if (cp.midpoint < 0.0)
+        cp.midpoint = 0.0;
+    if (cp.midpoint > 1.0)
+        cp.midpoint = 1.0;
+    if (cp.sharpness < 0.0)
+        cp.sharpness = 0.0;
+    if (cp.sharpness > 1.0)
+        cp.sharpness = 1.0;
+
+#ifdef DEBUG
+    TRACE("New opacity control point: %g = %g, %g %g",
+          cp.value, cp.alpha, cp.midpoint, cp.sharpness);
+#endif
+    for (std::list<OpacityControlPoint>::iterator itr = _opacityControlPoints.begin();
+         itr != _opacityControlPoints.end(); ++itr) {
+        if (itr->value == cp.value) {
+            *itr = cp;
+            return;
+        } else if (itr->value > cp.value) {
+            _opacityControlPoints.insert(itr, cp);
+            return;
+        }
+    }
+    // If we reach here, our control point goes at the end
+    _opacityControlPoints.insert(_opacityControlPoints.end(), cp);
+    _opacityTF->AddPoint(cp.value, cp.alpha, cp.midpoint, cp.sharpness);
+}
+
+/**
+ * \brief Set the number of discrete color table entries
+ *
+ * The number of table entries refers to the underlying 
+ * vtkLookupTable and is independent of the number of 
+ * control points in the transfer function.
+ */
+void ColorMap::setNumberOfTableEntries(int numEntries)
+{
+    if (numEntries != _numTableEntries) {
+        _needsBuild = true;
+        _numTableEntries = numEntries;
+        if (_lookupTable != NULL) {
+            build();
+        }
+    }
+}
+
+/**
+ * \brief Get the number of discrete color table entries
+ *
+ * The number of table entries refers to the underlying 
+ * vtkLookupTable and is independent of the number of 
+ * control points in the transfer function.
+ */
+int ColorMap::getNumberOfTableEntries()
+{
+    return _numTableEntries;
+}
+
+/**
+ * \brief Build the lookup table from the control points in the transfer 
+ * function
+ */
+void ColorMap::build()
+{
+    if (!_needsBuild)
+        return;
+
+    TRACE("%s", _name.c_str());
+
+    if (_lookupTable == NULL) {
+        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
+    }
+
+    _lookupTable->SetNumberOfTableValues(_numTableEntries);
+#if 0
+    std::list<ControlPoint>::iterator itr = _controlPoints.begin();
+    std::list<OpacityControlPoint>::iterator oitr = _opacityControlPoints.begin();
+
+    // If first value is > 0, insert a copy at 0 to create
+    // constant range up to first specified cp
+    if (itr->value > 0.0) {
+        ControlPoint cp = *itr;
+        cp.value = 0.0;
+        itr = _controlPoints.insert(itr, cp);
+    }
+    if (oitr->value > 0.0) {
+        OpacityControlPoint ocp = *oitr;
+        ocp.value = 0.0;
+        oitr = _opacityControlPoints.insert(oitr, ocp);
+    }
+
+    std::list<ControlPoint>::iterator itr2 = itr;
+    itr2++;
+    std::list<OpacityControlPoint>::iterator oitr2 = oitr;
+    oitr2++;
+
+    for (int i = 0; i < _numTableEntries; i++) {
+        double value = _numTableEntries < 2 ? 0.0 : ((double)i)/(_numTableEntries-1);
+        double color[4];
+        while (itr2 != _controlPoints.end() && value > itr2->value) {
+            itr = itr2;
+            itr2++;
+        }
+        while (oitr2 != _opacityControlPoints.end() && value > oitr2->value) {
+            oitr = oitr2;
+            oitr2++;
+        }
+        if (itr2 == _controlPoints.end()) {
+#ifdef DEBUG
+            TRACE("val: %g Range: %g - 1 Color: %g %g %g", value, itr->value,
+                  itr->color[0], itr->color[1], itr->color[2]);
+#endif
+            memcpy(color, itr->color, sizeof(double)*3);
+        } else {
+            assert(itr->value < itr2->value);
+            assert(value >= itr->value && value <= itr2->value);
+            lerp(color, *itr, *itr2, value);
+#ifdef DEBUG
+            TRACE("val: %g Range: %g - %g Color: %g %g %g", value, itr->value, itr2->value,
+                  color[0], color[1], color[2]);
+#endif
+        }
+        if (oitr2 == _opacityControlPoints.end()) {
+#ifdef DEBUG
+            TRACE("val: %g Range: %g - 1 Alpha %g", value, oitr->value,
+                  oitr->alpha);
+#endif
+            color[3] = oitr->alpha;
+        } else {
+            assert(oitr->value < oitr2->value);
+            assert(value >= oitr->value && value <= oitr2->value);
+            lerp(&color[3], *oitr, *oitr2, value);
+#ifdef DEBUG
+            TRACE("val: %g Range: %g - %g Alpha: %g", value, oitr->value, oitr2->value,
+                  color[3]);
+#endif
+        }
+        _lookupTable->SetTableValue(i, color);
+    }
+#else
+    double colorTable[_numTableEntries*3];
+    _colorTF->GetTable(0., 1., _numTableEntries, colorTable);
+    double opacityTable[_numTableEntries];
+    _opacityTF->GetTable(0., 1., _numTableEntries, opacityTable, 1);
+
+    for (int i = 0; i < _numTableEntries; i++) {
+        double color[4];
+        memcpy(color, colorTable+i*3, sizeof(double)*3);
+        color[3] = opacityTable[i];
+        _lookupTable->SetTableValue(i, color);
+    }
+#endif
+    _needsBuild = false;
+    TRACE("Leave");
+}
+
+/**
+ * \brief Perform linear interpolation of two color control points
+ */
+void ColorMap::lerp(double *result, 
+                    const ControlPoint& cp1,
+                    const ControlPoint& cp2, 
+                    double value)
+{
+    double factor = (value - cp1.value) / (cp2.value - cp1.value);
+    for (int i = 0; i < 3; i++) {
+        result[i] = cp1.color[i] * (1.0 - factor) + cp2.color[i] * factor;
+    }
+}
+
+/**
+ * \brief Perform linear interpolation of two opacity control points
+ */
+void ColorMap::lerp(double *result, 
+                    const OpacityControlPoint& cp1,
+                    const OpacityControlPoint& cp2,
+                    double value)
+{
+    double factor = (value - cp1.value) / (cp2.value - cp1.value);
+    *result = cp1.alpha * (1.0 - factor) + cp2.alpha * factor;
+}
+
+/**
+ * \brief Remove all control points and lookup table
+ */
+void ColorMap::clear()
+{
+    _controlPoints.clear();
+    _colorTF->RemoveAllPoints();
+    _opacityControlPoints.clear();
+    _opacityTF->RemoveAllPoints();
+    _lookupTable = NULL;
+}
+
+/**
+ * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
+ */
+ColorMap *ColorMap::getDefault()
+{
+    if (_default != NULL) {
+        return _default;
+    }
+
+    _default = new ColorMap("default");
+    ControlPoint cp[5];
+    cp[0].value = 0.0;
+    cp[0].color[0] = 0.0;
+    cp[0].color[1] = 0.0;
+    cp[0].color[2] = 1.0;
+    cp[1].value = 0.25;
+    cp[1].color[0] = 0.0;
+    cp[1].color[1] = 1.0;
+    cp[1].color[2] = 1.0;
+    cp[2].value = 0.5;
+    cp[2].color[0] = 0.0;
+    cp[2].color[1] = 1.0;
+    cp[2].color[2] = 0.0;
+    cp[3].value = 0.75;
+    cp[3].color[0] = 1.0;
+    cp[3].color[1] = 1.0;
+    cp[3].color[2] = 0.0;
+    cp[4].value = 1.0;
+    cp[4].color[0] = 1.0;
+    cp[4].color[1] = 0.0;
+    cp[4].color[2] = 0.0;
+    for (int i = 0; i < 5; i++) {
+        _default->addControlPoint(cp[i]);
+    }
+    OpacityControlPoint ocp[2];
+    ocp[0].value = 0.0;
+    ocp[0].alpha = 1.0;
+    ocp[1].value = 1.0;
+    ocp[1].alpha = 1.0;
+    _default->addOpacityControlPoint(ocp[0]);
+    _default->addOpacityControlPoint(ocp[1]);
+    _default->build();
+    return _default;
+}
+
+/**
+ * \brief Create a default ColorMap with a black-white grayscale ramp
+ */
+ColorMap *ColorMap::getGrayDefault()
+{
+    if (_grayDefault != NULL) {
+        return _grayDefault;
+    }
+
+    _grayDefault = new ColorMap("grayDefault");
+    ControlPoint cp[2];
+    cp[0].value = 0.0;
+    cp[0].color[0] = 0.0;
+    cp[0].color[1] = 0.0;
+    cp[0].color[2] = 0.0;
+    _grayDefault->addControlPoint(cp[0]);
+    cp[1].value = 1.0;
+    cp[1].color[0] = 1.0;
+    cp[1].color[1] = 1.0;
+    cp[1].color[2] = 1.0;
+    _grayDefault->addControlPoint(cp[1]);
+    OpacityControlPoint ocp[2];
+    ocp[0].value = 0.0;
+    ocp[0].alpha = 1.0;
+    ocp[1].value = 1.0;
+    ocp[1].alpha = 1.0;
+    _grayDefault->addOpacityControlPoint(ocp[0]);
+    _grayDefault->addOpacityControlPoint(ocp[1]);
+    _grayDefault->build();
+    return _grayDefault;
+}
+
+/**
+ * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
+ * and transparent to opaque ramp
+ */
+ColorMap *ColorMap::getVolumeDefault()
+{
+    if (_volumeDefault != NULL) {
+        return _volumeDefault;
+    }
+
+    _volumeDefault = new ColorMap("volumeDefault");
+    ControlPoint cp[5];
+    cp[0].value = 0.0;
+    cp[0].color[0] = 0.0;
+    cp[0].color[1] = 0.0;
+    cp[0].color[2] = 1.0;
+    cp[1].value = 0.25;
+    cp[1].color[0] = 0.0;
+    cp[1].color[1] = 1.0;
+    cp[1].color[2] = 1.0;
+    cp[2].value = 0.5;
+    cp[2].color[0] = 0.0;
+    cp[2].color[1] = 1.0;
+    cp[2].color[2] = 0.0;
+    cp[3].value = 0.75;
+    cp[3].color[0] = 1.0;
+    cp[3].color[1] = 1.0;
+    cp[3].color[2] = 0.0;
+    cp[4].value = 1.0;
+    cp[4].color[0] = 1.0;
+    cp[4].color[1] = 0.0;
+    cp[4].color[2] = 0.0;
+    for (int i = 0; i < 5; i++) {
+        _volumeDefault->addControlPoint(cp[i]);
+    }
+    OpacityControlPoint ocp[2];
+    ocp[0].value = 0.0;
+    ocp[0].alpha = 0.0;
+    ocp[1].value = 1.0;
+    ocp[1].alpha = 1.0;
+    _volumeDefault->addOpacityControlPoint(ocp[0]);
+    _volumeDefault->addOpacityControlPoint(ocp[1]);
+    _volumeDefault->build();
+    return _volumeDefault;
+}
+
+/**
+ * \brief Create a default ColorMap for coloring by atomic
+ * number in Molecules
+ */
+ColorMap *ColorMap::getElementDefault()
+{
+    if (_elementDefault != NULL) {
+        return _elementDefault;
+    }
+
+    _elementDefault = Molecule::createElementColorMap();
+    return _elementDefault;
+}
+
+/**
+ * \brief Render (using CPU) color map to an image
+ */
+void ColorMap::renderColorMap(ColorMap *map, int width, int height,
+                              vtkUnsignedCharArray *imgData,
+                              bool opaque, float bgColor[3],
+                              bool bgr, int bytesPerPixel)
+{
+    int size = bytesPerPixel * width * height;
+    if (imgData->GetMaxId() + 1 != size) {
+        imgData->SetNumberOfComponents(bytesPerPixel);
+        imgData->SetNumberOfValues(size);
+    }
+    unsigned char *dst = imgData->GetPointer(0);
+    vtkLookupTable *table = map->getLookupTable();
+    if (height > width) {
+        for (int i = 0; i < height; i++) {
+            double x = (double)i/(height-1);
+            double rgb[3];
+            table->GetColor(x, rgb);
+            unsigned char color[3];
+            if (opaque) {
+                color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
+                color[1] = (unsigned char)(255. * rgb[1]);
+                color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
+            } else {
+                double opacity = table->GetOpacity(x);
+                color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
+                color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
+                color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
+            }
+            for (int j = 0; j < width; j++) {
+                memcpy(dst, color, 3);
+                dst += 3;
+                if (bytesPerPixel == 4) {
+                    *(dst++) = (unsigned char)255.;
+                }
+            }
+        }
+    } else {
+        for (int i = 0; i < height; i++) {
+            for (int j = 0; j < width; j++) {
+                double x = (double)j/(width-1);
+                double rgb[3];
+                table->GetColor(x, rgb);
+                unsigned char color[3];
+                if (opaque) {
+                    color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
+                    color[1] = (unsigned char)(255. * rgb[1]);
+                    color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
+                } else {
+                    double opacity = table->GetOpacity(x);
+                    color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
+                    color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
+                    color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
+                }
+                memcpy(dst, color, 3);
+                dst += 3;
+                if (bytesPerPixel == 4) {
+                    *(dst++) = (unsigned char)255.;
+                }
+            }
+        }
+    }
+}
Index: vtkvis/tags/1.8.4/ColorMap.h
===================================================================
--- vtkvis/tags/1.8.4/ColorMap.h	(revision 6184)
+++ vtkvis/tags/1.8.4/ColorMap.h	(revision 6184)
@@ -0,0 +1,161 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_COLORMAP_H
+#define VTKVIS_COLORMAP_H
+
+#include <string>
+#include <list>
+#include <cstring>
+
+#include <vtkSmartPointer.h>
+#include <vtkColorTransferFunction.h>
+#include <vtkPiecewiseFunction.h>
+#include <vtkLookupTable.h>
+#include <vtkUnsignedCharArray.h>
+
+namespace VtkVis {
+
+/**
+ * \brief RGBA color map table created from transfer function
+ *
+ * ColorMap uses linear interpolation between ControlPoints to
+ * create a color lookup table for VTK
+ */
+class ColorMap {
+public:
+    /**
+     * \brief RGB inflection point in a transfer function
+     */
+    struct ControlPoint {
+        ControlPoint() :
+            value(0),
+            midpoint(0.5),
+            sharpness(0)
+        {
+            memset(color, 0, sizeof(double)*3);
+        }
+        ControlPoint(const ControlPoint& other) :
+            value(other.value),
+            midpoint(other.midpoint),
+            sharpness(other.sharpness)
+        {
+            memcpy(color, other.color, sizeof(double)*3);
+        }
+        ControlPoint& operator=(const ControlPoint &other)
+        {
+            if (this != &other) {
+                value = other.value;
+                midpoint = other.midpoint;
+                sharpness = other.sharpness;
+                memcpy(color, other.color, sizeof(double)*3);
+            }
+            return *this;
+        }
+
+        double value; ///< Normalized scalar data value [0,1]
+        double midpoint, sharpness;
+        double color[3]; ///< RGB color
+    };
+
+    /**
+     * \brief Opacity(alpha) inflection point in a transfer function
+     */
+    struct OpacityControlPoint {
+        OpacityControlPoint() :
+            value(0),
+            midpoint(0.5),
+            sharpness(0),
+            alpha(1)
+        {
+        }
+        OpacityControlPoint(const OpacityControlPoint& other) :
+            value(other.value),
+            midpoint(other.midpoint),
+            sharpness(other.sharpness),
+            alpha(other.alpha)
+        {
+        }
+        OpacityControlPoint& operator=(const OpacityControlPoint &other)
+        {
+            if (this != &other) {
+                value = other.value;
+                midpoint = other.midpoint;
+                sharpness = other.sharpness;
+                alpha = other.alpha;
+            }
+            return *this;
+        }
+
+        double value; ///< Normalized scalar data value [0,1]
+        double midpoint, sharpness;
+        double alpha; ///< Opacity
+    };
+
+    ColorMap(const std::string& name);
+
+    ColorMap(const ColorMap& other);
+
+    ColorMap& operator=(const ColorMap& other);
+
+    virtual ~ColorMap();
+
+    const std::string& getName();
+
+    vtkLookupTable *getLookupTable();
+
+    vtkSmartPointer<vtkColorTransferFunction> getColorTransferFunction(double range[2]);
+
+    vtkSmartPointer<vtkPiecewiseFunction> getOpacityTransferFunction(double range[2], double opacityScale = 1.0);
+
+    void setNumberOfTableEntries(int numEntries);
+
+    int getNumberOfTableEntries();
+
+    void addControlPoint(ControlPoint& cp);
+
+    void addOpacityControlPoint(OpacityControlPoint& cp);
+
+    void build();
+
+    void clear();
+
+    static ColorMap *getDefault();
+    static ColorMap *getGrayDefault();
+    static ColorMap *getVolumeDefault();
+    static ColorMap *getElementDefault();
+
+    static void renderColorMap(ColorMap *map, int width, int height,
+                               vtkUnsignedCharArray *imgData,
+                               bool opaque, float bgColor[3],
+                               bool bgr = false,
+                               int bytesPerPixel = 3);
+
+private:
+    static ColorMap *_default;
+    static ColorMap *_grayDefault;
+    static ColorMap *_volumeDefault;
+    static ColorMap *_elementDefault;
+
+    ColorMap();
+
+    void lerp(double *result, const ControlPoint& cp1, const ControlPoint& cp2, double value);
+    void lerp(double *result, const OpacityControlPoint& cp1, const OpacityControlPoint& cp2, double value);
+
+    std::string _name;
+    std::list<ControlPoint> _controlPoints;
+    std::list<OpacityControlPoint> _opacityControlPoints;
+    bool _needsBuild;
+    int _numTableEntries;
+    vtkSmartPointer<vtkColorTransferFunction> _colorTF;
+    vtkSmartPointer<vtkPiecewiseFunction> _opacityTF;
+    vtkSmartPointer<vtkLookupTable> _lookupTable;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Cone.cpp
===================================================================
--- vtkvis/tags/1.8.4/Cone.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Cone.cpp	(revision 6184)
@@ -0,0 +1,68 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkConeSource.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkReverseSense.h>
+
+#include "Cone.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Cone::Cone() :
+    Shape()
+{
+}
+
+Cone::~Cone()
+{
+    TRACE("Deleting Cone");
+}
+
+void Cone::update()
+{
+    if (_cone == NULL) {
+	_cone = vtkSmartPointer<vtkConeSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+    normalFilter->SetFeatureAngle(90.);
+    normalFilter->SetInputConnection(_cone->GetOutputPort());
+
+    _pdMapper->SetInputConnection(_cone->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+void Cone::flipNormals(bool state)
+{
+    if (_cone == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_cone->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_cone->GetOutputPort());
+    }
+}
Index: vtkvis/tags/1.8.4/Cone.h
===================================================================
--- vtkvis/tags/1.8.4/Cone.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Cone.h	(revision 6184)
@@ -0,0 +1,89 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_CONE_H
+#define VTKVIS_CONE_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkConeSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData cone
+ *
+ * This class creates a mesh cone
+ */
+class Cone : public Shape
+{
+public:
+    Cone();
+    virtual ~Cone();
+
+    virtual const char *getClassName() const
+    {
+        return "Cone";
+    }
+
+    void setCenter(double center[3])
+    {
+        if (_cone != NULL) {
+            _cone->SetCenter(center);
+	}
+    }
+
+    void setHeight(double height)
+    {
+        if (_cone != NULL) {
+            _cone->SetHeight(height);
+	}
+    }
+
+    void setRadius(double radius)
+    {
+        if (_cone != NULL) {
+            _cone->SetRadius(radius);
+	}
+    }
+
+    void setDirection(double x, double y, double z)
+    {
+        if (_cone != NULL) {
+            _cone->SetDirection(x, y, z);
+	}
+    }
+
+    void setResolution(int res)
+    {
+        if (_cone != NULL) {
+            _cone->SetResolution(res);
+	}
+    }
+
+    void setCapping(bool state)
+    {
+        if (_cone != NULL) {
+            _cone->SetCapping(state ? 1 : 0);
+	}
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkConeSource> _cone;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Contour2D.cpp
===================================================================
--- vtkvis/tags/1.8.4/Contour2D.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Contour2D.cpp	(revision 6184)
@@ -0,0 +1,575 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cfloat>
+
+#include <vtkVersion.h>
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCellDataToPointData.h>
+#include <vtkContourFilter.h>
+#include <vtkStripper.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkProperty.h>
+#include <vtkTransform.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkDataSetSurfaceFilter.h>
+
+#include "Contour2D.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Contour2D::Contour2D(int numContours) :
+    GraphicsObject(),
+    _numContours(numContours),
+    _pipelineInitialized(false),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _edgeColor[0] = _color[0];
+    _edgeColor[1] = _color[0];
+    _edgeColor[2] = _color[0];
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+    _lighting = false;
+}
+
+Contour2D::Contour2D(const std::vector<double>& contours) :
+    GraphicsObject(),
+    _numContours(contours.size()),
+    _contours(contours),
+    _pipelineInitialized(false),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _edgeColor[0] = _color[0];
+    _edgeColor[1] = _color[0];
+    _edgeColor[2] = _color[0];
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+    _lighting = false;
+}
+
+Contour2D::~Contour2D()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Contour2D for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Contour2D with NULL DataSet");
+#endif
+}
+
+void Contour2D::setDataSet(DataSet *dataSet,
+                           Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            const char *activeVectors = _dataSet->getActiveVectorsName();
+            if (activeVectors != NULL) {
+                renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3);
+                for (int i = 0; i < 3; i++) {
+                    renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                     _dataSet->getActiveVectorsName(),
+                                                     3, i);
+                }
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Internal method to re-compute contours after a state change
+ */
+void Contour2D::update()
+{
+    if (_dataSet == NULL) {
+        return;
+    }
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    // Contour filter to generate isolines
+    if (_contourFilter == NULL) {
+        _contourFilter = vtkSmartPointer<vtkContourFilter>::New();
+    }
+
+    if (!_pipelineInitialized) {
+        vtkAlgorithmOutput *dsOutput = NULL;
+        vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
+
+        if (ds->GetPointData() == NULL ||
+            ds->GetPointData()->GetScalars() == NULL) {
+            TRACE("No scalar point data in dataset %s", _dataSet->getName().c_str());
+            if (ds->GetCellData() != NULL &&
+                ds->GetCellData()->GetScalars() != NULL) {
+                cellToPtData = 
+                    vtkSmartPointer<vtkCellDataToPointData>::New();
+                cellToPtData->SetInputData(ds);
+                cellToPtData->PassCellDataOn();
+                dsOutput = cellToPtData->GetOutputPort();
+            } else {
+                USER_ERROR("No scalar field was found in the data set");
+            }
+        } else {
+            dsOutput = _dataSet->getProducerPort();
+        }
+
+        if (_dataSet->isCloud()) {
+            // DataSet is a point cloud
+            PrincipalPlane plane;
+            double offset;
+            if (_dataSet->is2D(&plane, &offset)) {
+                vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                if (plane == PLANE_ZY) {
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->RotateWXYZ(90, 0, 1, 0);
+                    if (offset != 0.0) {
+                        trans->Translate(-offset, 0, 0);
+                    }
+                    mesher->SetTransform(trans);
+                } else if (plane == PLANE_XZ) {
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->RotateWXYZ(-90, 1, 0, 0);
+                    if (offset != 0.0) {
+                        trans->Translate(0, -offset, 0);
+                    }
+                    mesher->SetTransform(trans);
+                } else if (offset != 0.0) {
+                    // XY with Z offset
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->Translate(0, 0, -offset);
+                    mesher->SetTransform(trans);
+                }
+                mesher->SetInputConnection(dsOutput);
+                _contourFilter->SetInputConnection(mesher->GetOutputPort());
+            } else {
+                vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+                mesher->SetInputConnection(dsOutput);
+                vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                gf->SetInputConnection(mesher->GetOutputPort());
+                _contourFilter->SetInputConnection(gf->GetOutputPort());
+            }
+        } else if (vtkPolyData::SafeDownCast(ds) != NULL) {
+            // DataSet is a vtkPolyData with lines and/or polygons
+            _contourFilter->SetInputConnection(dsOutput);
+        } else {
+            TRACE("Generating surface for data set");
+            // DataSet is NOT a vtkPolyData
+            vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+            gf->SetInputConnection(dsOutput);
+            _contourFilter->SetInputConnection(gf->GetOutputPort());
+        }
+    }
+
+    _pipelineInitialized = true;
+
+    _contourFilter->ComputeNormalsOff();
+    _contourFilter->ComputeGradientsOff();
+
+    // Speed up multiple contour computation at cost of extra memory use
+    if (_numContours > 1) {
+        _contourFilter->UseScalarTreeOn();
+    } else {
+        _contourFilter->UseScalarTreeOff();
+    }
+
+    _contourFilter->SetNumberOfContours(_numContours);
+
+    if (_contours.empty()) {
+        // Evenly spaced isovalues
+        _contourFilter->GenerateValues(_numContours, _dataRange[0], _dataRange[1]);
+    } else {
+        // User-supplied isovalues
+        for (int i = 0; i < _numContours; i++) {
+            _contourFilter->SetValue(i, _contours[i]);
+        }
+    }
+
+    initProp();
+
+    if (_mapper == NULL) {
+        _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _mapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _mapper->ScalarVisibilityOff();
+        vtkSmartPointer<vtkStripper> stripper = vtkSmartPointer<vtkStripper>::New();
+        stripper->SetInputConnection(_contourFilter->GetOutputPort());
+        _mapper->SetInputConnection(stripper->GetOutputPort());
+        getActor()->SetMapper(_mapper);
+    }
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+        setColorMode(_colorMode);
+    }
+
+    _mapper->Update();
+}
+
+void Contour2D::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        const char *activeVectors = _dataSet->getActiveVectorsName();
+        if (activeVectors != NULL) {
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+ 
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+
+    if (_contours.empty() && _numContours > 0) {
+        // Contour isovalues need to be recomputed
+        update();
+    }
+}
+
+void Contour2D::setContourField(const char *name)
+{
+    if (_contourFilter != NULL) {
+        _contourFilter->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, name);
+        update();
+    }
+}
+
+void Contour2D::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void Contour2D::setColorMode(ColorMode mode,
+                             const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void Contour2D::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                             const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _mapper == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        _mapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        _mapper->SetScalarModeToUseCellFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (name != NULL && strlen(name) > 0) {
+        _mapper->SelectColorArray(name);
+    } else {
+        _mapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _mapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _mapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void Contour2D::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Contour2D::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_mapper != NULL) {
+            _mapper->UseLookupTableScalarRangeOn();
+            _mapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Specify number of evenly spaced contour lines to render
+ *
+ * Will override any existing contours
+ */
+void Contour2D::setNumContours(int numContours)
+{
+    _contours.clear();
+    _numContours = numContours;
+
+    update();
+}
+
+/**
+ * \brief Specify an explicit list of contour isovalues
+ *
+ * Will override any existing contours
+ */
+void Contour2D::setContourList(const std::vector<double>& contours)
+{
+    _contours = contours;
+    _numContours = (int)_contours.size();
+
+    update();
+}
+
+/**
+ * \brief Get the number of contours
+ */
+int Contour2D::getNumContours() const
+{
+    return _numContours;
+}
+
+/**
+ * \brief Get the contour list (may be empty if number of contours
+ * was specified in place of a list)
+ */
+const std::vector<double>& Contour2D::getContourList() const
+{
+    return _contours;
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Contour2D::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_mapper != NULL) {
+        _mapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Contour2D.h
===================================================================
--- vtkvis/tags/1.8.4/Contour2D.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Contour2D.h	(revision 6184)
@@ -0,0 +1,124 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_CONTOUR2D_H
+#define VTKVIS_CONTOUR2D_H
+
+#include <vtkSmartPointer.h>
+#include <vtkContourFilter.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkPlaneCollection.h>
+#include <vtkLookupTable.h>
+
+#include <vector>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief 2D Contour lines (isolines)
+ */
+class Contour2D : public GraphicsObject {
+public:
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+
+    Contour2D(int numContours);
+
+    Contour2D(const std::vector<double>& contours);
+
+    virtual ~Contour2D();
+
+    virtual const char *getClassName() const
+    {
+        return "Contour2D";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setContourField(const char *name);
+
+    void setNumContours(int numContours);
+
+    void setContourList(const std::vector<double>& contours);
+
+    int getNumContours() const;
+
+    const std::vector<double>& getContourList() const;
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+    virtual void setColor(float color[3])
+    {
+        GraphicsObject::setColor(color);
+        GraphicsObject::setEdgeColor(color);
+    }
+
+    virtual void setEdgeColor(float color[3])
+    {
+        setColor(color);
+    }
+
+private:
+    Contour2D();
+
+    virtual void update();
+
+    int _numContours;
+    std::vector<double> _contours;
+
+    bool _pipelineInitialized;
+
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkContourFilter> _contourFilter;
+    vtkSmartPointer<vtkPolyDataMapper> _mapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Contour3D.cpp
===================================================================
--- vtkvis/tags/1.8.4/Contour3D.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Contour3D.cpp	(revision 6184)
@@ -0,0 +1,548 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkVersion.h>
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCellDataToPointData.h>
+#include <vtkContourFilter.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkProperty.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkDataSetSurfaceFilter.h>
+
+#include "Contour3D.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Contour3D::Contour3D(int numContours) :
+    GraphicsObject(),
+    _numContours(numContours),
+    _pipelineInitialized(false),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+Contour3D::Contour3D(const std::vector<double>& contours) :
+    GraphicsObject(),
+    _numContours(contours.size()),
+    _contours(contours),
+    _pipelineInitialized(false),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+Contour3D::~Contour3D()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Contour3D for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Contour3D with NULL DataSet");
+#endif
+}
+
+void Contour3D::setDataSet(DataSet *dataSet,
+                           Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            const char *activeVectors = _dataSet->getActiveVectorsName();
+            if (activeVectors != NULL) {
+                renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3);
+                for (int i = 0; i < 3; i++) {
+                    renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                     _dataSet->getActiveVectorsName(),
+                                                     3, i);
+                }
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Internal method to re-compute contours after a state change
+ */
+void Contour3D::update()
+{
+    if (_dataSet == NULL) {
+        return;
+    }
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    if (_dataSet->is2D()) {
+        USER_ERROR("Cannot create isosurface(s) since the data set is not 3D");
+        return;
+    }
+
+    // Contour filter to generate isosurfaces
+    if (_contourFilter == NULL) {
+        _contourFilter = vtkSmartPointer<vtkContourFilter>::New();
+    }
+
+    if (!_pipelineInitialized) {
+        vtkAlgorithmOutput *dsOutput = NULL;
+        vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
+
+        if (ds->GetPointData() == NULL ||
+            ds->GetPointData()->GetScalars() == NULL) {
+            TRACE("No scalar point data in dataset %s", _dataSet->getName().c_str());
+            if (ds->GetCellData() != NULL &&
+                ds->GetCellData()->GetScalars() != NULL) {
+                cellToPtData = 
+                    vtkSmartPointer<vtkCellDataToPointData>::New();
+                cellToPtData->SetInputData(ds);
+                cellToPtData->PassCellDataOn();
+                dsOutput = cellToPtData->GetOutputPort();
+            } else {
+                USER_ERROR("No scalar field was found in the data set.");
+            }
+        } else {
+            dsOutput = _dataSet->getProducerPort();
+        }
+
+        if (_dataSet->isCloud()) {
+            // DataSet is a point cloud
+            // Generate a 3D unstructured grid
+            vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+            mesher->SetInputConnection(dsOutput);
+            _contourFilter->SetInputConnection(mesher->GetOutputPort());
+        } else {
+            // DataSet is 3D with cells.  If DataSet is a surface
+            // (e.g. polydata or ugrid with 2D cells), we will 
+            // generate lines instead of surfaces
+            _contourFilter->SetInputConnection(dsOutput);
+        }
+    }
+
+    _pipelineInitialized = true;
+
+    _contourFilter->ComputeNormalsOff();
+    _contourFilter->ComputeGradientsOff();
+
+    // Speed up multiple contour computation at cost of extra memory use
+    if (_numContours > 1) {
+        _contourFilter->UseScalarTreeOn();
+    } else {
+        _contourFilter->UseScalarTreeOff();
+    }
+
+    _contourFilter->SetNumberOfContours(_numContours);
+
+    if (_contours.empty()) {
+        // Evenly spaced isovalues
+        TRACE("Setting %d contours between %g and %g", _numContours, _dataRange[0], _dataRange[1]);
+        _contourFilter->GenerateValues(_numContours, _dataRange[0], _dataRange[1]);
+    } else {
+        // User-supplied isovalues
+        for (int i = 0; i < _numContours; i++) {
+            _contourFilter->SetValue(i, _contours[i]);
+        }
+    }
+
+    initProp();
+
+    if (_normalsGenerator == NULL) {
+        _normalsGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
+        _normalsGenerator->SetInputConnection(_contourFilter->GetOutputPort());
+        _normalsGenerator->SetFeatureAngle(90.);
+        _normalsGenerator->AutoOrientNormalsOff();
+        _normalsGenerator->ComputePointNormalsOn();
+    }
+
+    if (_mapper == NULL) {
+        _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _mapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _mapper->SetInputConnection(_normalsGenerator->GetOutputPort());
+        _mapper->ScalarVisibilityOff();
+        //_mapper->SetColorModeToMapScalars();
+        getActor()->SetMapper(_mapper);
+    }
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+        setColorMode(_colorMode);
+    }
+
+    _mapper->Update();
+    TRACE("Contour output %d polys, %d strips",
+          _contourFilter->GetOutput()->GetNumberOfPolys(),
+          _contourFilter->GetOutput()->GetNumberOfStrips());
+}
+
+void Contour3D::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        const char *activeVectors = _dataSet->getActiveVectorsName();
+        if (activeVectors != NULL) {
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+ 
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+
+    if (_contours.empty() && _numContours > 0) {
+        // Contour isovalues need to be recomputed
+        update();
+    }
+}
+
+void Contour3D::setContourField(const char *name)
+{
+    if (_contourFilter != NULL) {
+        _contourFilter->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, name);
+        update();
+    }
+}
+
+void Contour3D::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void Contour3D::setColorMode(ColorMode mode,
+                             const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void Contour3D::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                             const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _mapper == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        _mapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        _mapper->SetScalarModeToUseCellFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (name != NULL && strlen(name) > 0) {
+        _mapper->SelectColorArray(name);
+    } else {
+        _mapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _mapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _mapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void Contour3D::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Contour3D::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_mapper != NULL) {
+            _mapper->UseLookupTableScalarRangeOn();
+            _mapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Specify number of evenly spaced isosurfaces to render
+ *
+ * Will override any existing contours
+ */
+void Contour3D::setNumContours(int numContours)
+{
+    _contours.clear();
+    _numContours = numContours;
+
+    update();
+}
+
+/**
+ * \brief Specify an explicit list of contour isovalues
+ *
+ * Will override any existing contours
+ */
+void Contour3D::setContourList(const std::vector<double>& contours)
+{
+    _contours = contours;
+    _numContours = (int)_contours.size();
+
+    update();
+}
+
+/**
+ * \brief Get the number of contours
+ */
+int Contour3D::getNumContours() const
+{
+    return _numContours;
+}
+
+/**
+ * \brief Get the contour list (may be empty if number of contours
+ * was specified in place of a list)
+ */
+const std::vector<double>& Contour3D::getContourList() const
+{
+    return _contours;
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Contour3D::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_mapper != NULL) {
+        _mapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Contour3D.h
===================================================================
--- vtkvis/tags/1.8.4/Contour3D.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Contour3D.h	(revision 6184)
@@ -0,0 +1,115 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_CONTOUR3D_H
+#define VTKVIS_CONTOUR3D_H
+
+#include <vtkSmartPointer.h>
+#include <vtkContourFilter.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkLookupTable.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkPlaneCollection.h>
+
+#include <vector>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief 3D Contour isosurfaces (geometry)
+ */
+class Contour3D : public GraphicsObject {
+public:
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+
+    Contour3D(int numContours);
+
+    Contour3D(const std::vector<double>& contours);
+
+    virtual ~Contour3D();
+
+    virtual const char *getClassName() const
+    {
+        return "Contour3D";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setNumContours(int numContours);
+
+    void setContourList(const std::vector<double>& contours);
+
+    int getNumContours() const;
+
+    const std::vector<double>& getContourList() const;
+
+    void setContourField(const char *name);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    Contour3D();
+
+    virtual void update();
+
+    int _numContours;
+    std::vector<double> _contours;
+
+    bool _pipelineInitialized;
+
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkContourFilter> _contourFilter;
+    vtkSmartPointer<vtkPolyDataNormals> _normalsGenerator;
+    vtkSmartPointer<vtkPolyDataMapper> _mapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Cutplane.cpp
===================================================================
--- vtkvis/tags/1.8.4/Cutplane.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Cutplane.cpp	(revision 6184)
@@ -0,0 +1,876 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+#include <cfloat>
+#include <cstring>
+
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkProperty.h>
+#include <vtkImageData.h>
+#include <vtkLookupTable.h>
+#include <vtkTransform.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkGaussianSplatter.h>
+#include <vtkExtractVOI.h>
+#include <vtkCutter.h>
+#include <vtkDataSetSurfaceFilter.h>
+
+#include "Cutplane.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Cutplane::Cutplane() :
+    GraphicsObject(),
+    _pipelineInitialized(false),
+    _cloudStyle(CLOUD_MESH),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+Cutplane::~Cutplane()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Cutplane for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Cutplane with NULL DataSet");
+#endif
+}
+
+void Cutplane::setDataSet(DataSet *dataSet,
+                          Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Create and initialize a VTK Prop to render the object
+ */
+void Cutplane::initProp()
+{
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkAssembly>::New();
+
+        for (int i = 0; i < 3; i++) {
+            _actor[i] = vtkSmartPointer<vtkActor>::New();
+            _borderActor[i] = vtkSmartPointer<vtkActor>::New();
+            //_actor[i]->VisibilityOff();
+            vtkProperty *property = _actor[i]->GetProperty();
+            property->SetColor(_color[0], _color[1], _color[2]);
+            property->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+            property->SetLineWidth(_edgeWidth);
+            property->SetPointSize(_pointSize);
+            property->EdgeVisibilityOff();
+            property->SetOpacity(_opacity);
+            property->SetAmbient(.2);
+            if (!_lighting)
+                property->LightingOff();
+            if (_faceCulling && _opacity == 1.0) {
+                setCulling(property, true);
+            }
+            property = _borderActor[i]->GetProperty();
+            property->SetColor(_color[0], _color[1], _color[2]);
+            property->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+            property->SetLineWidth(_edgeWidth);
+            property->SetPointSize(_pointSize);
+            property->EdgeVisibilityOff();
+            property->SetOpacity(_opacity);
+            property->SetAmbient(.2);
+            property->LightingOff();
+            
+            getAssembly()->AddPart(_borderActor[i]);
+        }
+    } else {
+        for (int i = 0; i < 3; i++) {
+            getAssembly()->RemovePart(_actor[i]);
+        }
+    }
+}
+
+/**
+ * \brief Set the material color (sets ambient, diffuse, and specular)
+ */
+void Cutplane::setColor(float color[3])
+{
+    for (int i = 0; i < 3; i++)
+        _color[i] = color[i];
+
+    for (int i = 0; i < 3; i++) {
+        if (_borderActor[i] != NULL) {
+            _borderActor[i]->GetProperty()->SetColor(color[0], color[1], color[2]);
+        }
+    }
+}
+
+void Cutplane::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    double bounds[6];
+    _dataSet->getBounds(bounds);
+    // Mapper, actor to render color-mapped data set
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] == NULL) {
+            _mapper[i] = vtkSmartPointer<vtkPolyDataMapper>::New();
+            // Map scalars through lookup table regardless of type
+            _mapper[i]->SetColorModeToMapScalars();
+        }
+        if (_cutPlane[i] == NULL) {
+            _cutPlane[i] = vtkSmartPointer<vtkPlane>::New();
+            switch (i) {
+            case 0:
+                _cutPlane[i]->SetNormal(1, 0, 0);
+                _cutPlane[i]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])/2.,
+                                        0,
+                                        0);
+                break;
+            case 1:
+                _cutPlane[i]->SetNormal(0, 1, 0);
+                _cutPlane[i]->SetOrigin(0,
+                                        bounds[2] + (bounds[3]-bounds[2])/2.,
+                                        0);
+                break;
+            case 2:
+            default:
+                _cutPlane[i]->SetNormal(0, 0, 1);
+                _cutPlane[i]->SetOrigin(0,
+                                        0,
+                                        bounds[4] + (bounds[5]-bounds[4])/2.);
+                break;
+            }
+        }
+        if (_cutter[i] == NULL) {
+            _cutter[i] = vtkSmartPointer<vtkCutter>::New();
+            _cutter[i]->SetCutFunction(_cutPlane[i]);
+        }
+    }
+
+    initProp();
+
+    if (!_pipelineInitialized) {
+        _splatter = NULL;
+        if (_dataSet->isCloud()) {
+            // DataSet is a point cloud
+            PrincipalPlane plane;
+            double offset;
+            if (_dataSet->is2D(&plane, &offset)) {
+                // DataSet is a 2D point cloud
+                if (_cloudStyle == CLOUD_MESH) {
+                    vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                    if (plane == PLANE_ZY) {
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->RotateWXYZ(90, 0, 1, 0);
+                        if (offset != 0.0) {
+                            trans->Translate(-offset, 0, 0);
+                        }
+                        mesher->SetTransform(trans);
+                        _actor[1]->VisibilityOff();
+                        _actor[2]->VisibilityOff();
+                    } else if (plane == PLANE_XZ) {
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->RotateWXYZ(-90, 1, 0, 0);
+                        if (offset != 0.0) {
+                            trans->Translate(0, -offset, 0);
+                        }
+                        mesher->SetTransform(trans);
+                        _actor[0]->VisibilityOff();
+                        _actor[2]->VisibilityOff();
+                    } else if (offset != 0.0) {
+                        // XY with Z offset
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->Translate(0, 0, -offset);
+                        mesher->SetTransform(trans);
+                        _actor[0]->VisibilityOff();
+                        _actor[1]->VisibilityOff();
+                    }
+                    mesher->SetInputData(ds);
+                    for (int i = 0; i < 3; i++) {
+                        _mapper[i]->SetInputConnection(mesher->GetOutputPort());
+                    }
+                } else {
+                    // _cloudStyle == CLOUD_SPLAT
+                    if (_splatter == NULL) {
+                        _splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                    }
+                    _splatter->SetInputData(ds);
+                    int dims[3];
+                    _splatter->GetSampleDimensions(dims);
+                    TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
+                    if (plane == PLANE_ZY) {
+                        dims[0] = 3;
+                    } else if (plane == PLANE_XZ) {
+                        dims[1] = 3;
+                    } else {
+                        dims[2] = 3;
+                    }
+                    _splatter->SetSampleDimensions(dims);
+                    for (int i = 0; i < 3; i++) {
+                        _cutter[i]->SetInputConnection(_splatter->GetOutputPort());
+                        vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                        gf->UseStripsOn();
+                        gf->SetInputConnection(_cutter[i]->GetOutputPort());
+                        _mapper[i]->SetInputConnection(gf->GetOutputPort());
+                    }
+                }
+            } else {
+                if (_cloudStyle == CLOUD_MESH) {
+                    // Data Set is a 3D point cloud
+                    // Result of Delaunay3D mesher is unstructured grid
+                    vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+                    mesher->SetInputData(ds);
+                    // Sample a plane within the grid bounding box
+                    for (int i = 0; i < 3; i++) {
+                        _cutter[i]->SetInputConnection(mesher->GetOutputPort());
+                        vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                        gf->UseStripsOn();
+                        gf->SetInputConnection(_cutter[i]->GetOutputPort());
+                        _mapper[i]->SetInputConnection(gf->GetOutputPort());
+                    }
+                } else {
+                    // _cloudStyle == CLOUD_SPLAT
+                    if (_splatter == NULL) {
+                        _splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                    }
+                    _splatter->SetInputData(ds);
+                    int dims[3];
+                    dims[0] = dims[1] = dims[2] = 64;
+                    TRACE("Generating volume with dims (%d,%d,%d) from point cloud",
+                          dims[0], dims[1], dims[2]);
+                    _splatter->SetSampleDimensions(dims);
+                    for (int i = 0; i < 3; i++) {
+                        _cutter[i]->SetInputConnection(_splatter->GetOutputPort());
+                        vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                        gf->UseStripsOn();
+                        gf->SetInputConnection(_cutter[i]->GetOutputPort());
+                        _mapper[i]->SetInputConnection(gf->GetOutputPort());
+                    }
+                }
+            }
+        } else {
+            // DataSet can be: image/volume/uniform grid, structured grid, unstructured grid, rectilinear grid, or
+            // PolyData with cells other than points
+            PrincipalPlane plane;
+            double offset;
+            if (!_dataSet->is2D(&plane, &offset)) {
+                // Sample a plane within the grid bounding box
+                for (int i = 0; i < 3; i++) {
+                    _cutter[i]->SetInputData(ds);
+                    vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                    gf->UseStripsOn();
+                    gf->SetInputConnection(_cutter[i]->GetOutputPort());
+                    _mapper[i]->SetInputConnection(gf->GetOutputPort());
+                }
+            } else {
+                // 2D data
+                if (plane == PLANE_ZY) {
+                    _actor[1]->VisibilityOff();
+                    _actor[2]->VisibilityOff();
+                } else if (plane == PLANE_XZ) {
+                    _actor[0]->VisibilityOff();
+                    _actor[2]->VisibilityOff();
+                } else if (offset != 0.0) {
+                    // XY with Z offset
+                    _actor[0]->VisibilityOff();
+                    _actor[1]->VisibilityOff();
+                }
+                for (int i = 0; i < 3; i++) {
+                    vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                    gf->UseStripsOn();
+                    gf->SetInputData(ds);
+                    _mapper[i]->SetInputConnection(gf->GetOutputPort());
+                }
+            }
+        }
+    }
+
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] != NULL && _borderMapper[i] == NULL) {
+            _borderMapper[i] = vtkSmartPointer<vtkPolyDataMapper>::New();
+#ifdef CUTPLANE_TIGHT_OUTLINE
+            _outlineFilter[i] = vtkSmartPointer<vtkOutlineFilter>::New();
+            _outlineFilter[i]->SetInputConnection(_mapper[i]->GetInputConnection(0, 0));
+            _borderMapper[i]->SetInputConnection(_outlineFilter[i]->GetOutputPort());
+#else
+            _outlineSource[i] = vtkSmartPointer<vtkOutlineSource>::New();
+            switch (i) {
+            case 0:
+                _outlineSource[i]->SetBounds(bounds[0] + (bounds[1]-bounds[0])/2.,
+                                             bounds[0] + (bounds[1]-bounds[0])/2.,
+                                             bounds[2], bounds[3],
+                                             bounds[4], bounds[5]);
+                break;
+            case 1:
+                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
+                                             bounds[2] + (bounds[3]-bounds[2])/2.,
+                                             bounds[2] + (bounds[3]-bounds[2])/2.,
+                                             bounds[4], bounds[5]);
+                break;
+            case 2:
+                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
+                                             bounds[2], bounds[3],
+                                             bounds[4] + (bounds[5]-bounds[4])/2.,
+                                             bounds[4] + (bounds[5]-bounds[4])/2.);
+                break;
+            default:
+                ;
+            }
+            _borderMapper[i]->SetInputConnection(_outlineSource[i]->GetOutputPort());
+#endif
+            _borderMapper[i]->SetResolveCoincidentTopologyToPolygonOffset();
+        }
+    }
+
+    setInterpolateBeforeMapping(true);
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+        if (ds->GetPointData()->GetScalars() == NULL &&
+            ds->GetPointData()->GetVectors() != NULL) {
+            TRACE("Setting color mode to vector magnitude");
+            setColorMode(COLOR_BY_VECTOR_MAGNITUDE);
+        } else {
+            TRACE("Setting color mode to scalar");
+            setColorMode(COLOR_BY_SCALAR);
+        }
+    } else if (!_pipelineInitialized) {
+        double *rangePtr = _colorFieldRange;
+        if (_colorFieldRange[0] > _colorFieldRange[1]) {
+            rangePtr = NULL;
+        }
+        setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+    }
+
+    _pipelineInitialized = true;
+
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] != NULL) {
+            _actor[i]->SetMapper(_mapper[i]);
+            _mapper[i]->Update();
+        }
+        if (_borderMapper[i] != NULL) {
+            _borderActor[i]->SetMapper(_borderMapper[i]);
+            _borderMapper[i]->Update();
+        }
+        // Only add cutter actor to assembly if geometry was
+        // produced, in order to prevent messing up assembly bounds
+        double bounds[6];
+        _actor[i]->GetBounds(bounds);
+        if (bounds[0] <= bounds[1]) {
+            getAssembly()->AddPart(_actor[i]);
+        }
+    }
+}
+
+void Cutplane::setCloudStyle(CloudStyle style)
+{
+    if (style != _cloudStyle) {
+        _cloudStyle = style;
+        if (_dataSet != NULL) {
+            _pipelineInitialized = false;
+            update();
+        }
+    }
+}
+
+void Cutplane::setInterpolateBeforeMapping(bool state)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] != NULL) {
+            _mapper[i]->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
+        }
+    }
+}
+
+/**
+ * \brief Select a 2D slice plane from a 3D DataSet
+ *
+ * \param[in] axis Axis of slice plane
+ * \param[in] ratio Position [0,1] of slice plane along axis
+ */
+void Cutplane::selectVolumeSlice(Axis axis, double ratio)
+{
+    if (_dataSet->is2D()) {
+        WARN("DataSet not 3D, returning");
+        return;
+    }
+
+    if ((axis == X_AXIS &&_cutPlane[0] == NULL) ||
+        (axis == Y_AXIS &&_cutPlane[1] == NULL) ||
+        (axis == Z_AXIS &&_cutPlane[2] == NULL)) {
+        WARN("Called before update() or DataSet is not a volume");
+        return;
+    }
+
+    double bounds[6];
+    _dataSet->getBounds(bounds);
+#if 0
+    if (ratio == 0.0)
+        ratio = 0.001;
+    if (ratio == 1.0)
+        ratio = 0.999;
+#endif
+    switch (axis) {
+    case X_AXIS:
+        _cutPlane[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0]) * ratio,
+                                0,
+                                0);
+#ifndef CUTPLANE_TIGHT_OUTLINE
+        _outlineSource[0]->SetBounds(bounds[0] + (bounds[1]-bounds[0]) * ratio,
+                                     bounds[0] + (bounds[1]-bounds[0]) * ratio,
+                                     bounds[2], bounds[3],
+                                     bounds[4], bounds[5]);
+#endif
+        break;
+    case Y_AXIS:
+        _cutPlane[1]->SetOrigin(0,
+                                bounds[2] + (bounds[3]-bounds[2]) * ratio,
+                                0);
+#ifndef CUTPLANE_TIGHT_OUTLINE
+        _outlineSource[1]->SetBounds(bounds[0], bounds[1],
+                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
+                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
+                                     bounds[4], bounds[5]);
+#endif
+        break;
+    case Z_AXIS:
+        _cutPlane[2]->SetOrigin(0,
+                                0,
+                                bounds[4] + (bounds[5]-bounds[4]) * ratio);
+#ifndef CUTPLANE_TIGHT_OUTLINE
+        _outlineSource[2]->SetBounds(bounds[0], bounds[1],
+                                     bounds[2], bounds[3],
+                                     bounds[4] + (bounds[5]-bounds[4]) * ratio,
+                                     bounds[4] + (bounds[5]-bounds[4]) * ratio);
+#endif
+        break;
+    default:
+        ERROR("Invalid Axis");
+        return;
+    }
+    update();
+}
+
+void Cutplane::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                         _dataSet->getActiveVectorsName(),
+                                         3);
+        for (int i = 0; i < 3; i++) {
+            renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                             _dataSet->getActiveVectorsName(),
+                                             3, i);
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+}
+
+void Cutplane::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    default:
+        ;
+    }
+}
+
+void Cutplane::setColorMode(ColorMode mode,
+                            const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type;
+    int numComponents;
+    if (!_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void Cutplane::setColorMode(ColorMode mode,
+                            DataSet::DataAttributeType type,
+                            const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL ||
+        _mapper[0] == NULL ||
+        _mapper[1] == NULL ||
+        _mapper[2] == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->SetScalarModeToUsePointFieldData();
+        }
+        break;
+    case DataSet::CELL_DATA:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->SetScalarModeToUseCellFieldData();
+        }
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (_splatter != NULL) {
+        if (name != NULL && strlen(name) > 0) {
+            _splatter->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, name);
+        }
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->SelectColorArray("SplatterValues");
+        }
+    } else if (name != NULL && strlen(name) > 0) {
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->SelectColorArray(name);
+        }
+    } else {
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->SetScalarModeToDefault();
+        }
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->ScalarVisibilityOn();
+        }
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->ScalarVisibilityOn();
+        }
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->ScalarVisibilityOn();
+        }
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->ScalarVisibilityOn();
+        }
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->ScalarVisibilityOn();
+        }
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    default:
+        for (int i = 0; i < 3; i++) {
+            _mapper[i]->ScalarVisibilityOff();
+        }
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void Cutplane::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Cutplane::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        for (int i = 0; i < 3; i++) {
+            if (_mapper[i] != NULL) {
+                _mapper[i]->UseLookupTableScalarRangeOn();
+                _mapper[i]->SetLookupTable(_lut);
+            }
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Turn on/off lighting of this object
+ */
+void Cutplane::setLighting(bool state)
+{
+    _lighting = state;
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL)
+            _actor[i]->GetProperty()->SetLighting((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of mesh edges
+ */
+void Cutplane::setEdgeVisibility(bool state)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
+        }
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of outlines
+ */
+void Cutplane::setOutlineVisibility(bool state)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_borderActor[i] != NULL) {
+            _borderActor[i]->SetVisibility((state ? 1 : 0));
+        }
+    }
+}
+
+/**
+ * \brief Set visibility of cutplane on specified axis
+ */
+void Cutplane::setSliceVisibility(Axis axis, bool state)
+{
+    switch (axis) {
+    case X_AXIS:
+        if (_actor[0] != NULL)
+            _actor[0]->SetVisibility((state ? 1 : 0));
+        if (_borderActor[0] != NULL)
+            _borderActor[0]->SetVisibility((state ? 1 : 0));
+        break;
+    case Y_AXIS:
+        if (_actor[1] != NULL)
+            _actor[1]->SetVisibility((state ? 1 : 0));
+        if (_borderActor[1] != NULL)
+            _borderActor[1]->SetVisibility((state ? 1 : 0));
+        break;
+    case Z_AXIS:
+    default:
+        if (_actor[2] != NULL)
+            _actor[2]->SetVisibility((state ? 1 : 0));
+        if (_borderActor[2] != NULL)
+            _borderActor[2]->SetVisibility((state ? 1 : 0));
+        break;
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Cutplane::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] != NULL) {
+            _mapper[i]->SetClippingPlanes(planes);
+        }
+        if (_borderMapper[i] != NULL) {
+            _borderMapper[i]->SetClippingPlanes(planes);
+        }
+    }
+}
Index: vtkvis/tags/1.8.4/Cutplane.h
===================================================================
--- vtkvis/tags/1.8.4/Cutplane.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Cutplane.h	(revision 6184)
@@ -0,0 +1,133 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_CUTPLANE_H
+#define VTKVIS_CUTPLANE_H
+
+#include <vtkSmartPointer.h>
+#include <vtkLookupTable.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkPlaneCollection.h>
+#include <vtkCutter.h>
+#include <vtkPlane.h>
+#include <vtkOutlineFilter.h>
+#include <vtkOutlineSource.h>
+#include <vtkGaussianSplatter.h>
+
+#include "ColorMap.h"
+#include "Types.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+//#define CUTPLANE_TIGHT_OUTLINE
+
+/**
+ * \brief Color-mapped plot of slice through a data set
+ * 
+ * Currently the DataSet must be image data (2D uniform grid)
+ */
+class Cutplane : public GraphicsObject {
+public:
+    enum CloudStyle {
+        CLOUD_MESH,
+        CLOUD_SPLAT
+    };
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z
+    };
+
+    Cutplane();
+    virtual ~Cutplane();
+
+    virtual const char *getClassName() const
+    {
+        return "Cutplane";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setColor(float color[3]);
+
+    virtual void setLighting(bool state);
+
+    virtual void setEdgeVisibility(bool state);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setCloudStyle(CloudStyle style);
+
+    void setOutlineVisibility(bool state);
+
+    void selectVolumeSlice(Axis axis, double ratio);
+
+    void setSliceVisibility(Axis axis, bool state);
+
+    void setInterpolateBeforeMapping(bool state);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    virtual void initProp();
+    virtual void update();
+
+    bool _pipelineInitialized;
+
+    CloudStyle _cloudStyle;
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkActor> _actor[3];
+    vtkSmartPointer<vtkActor> _borderActor[3];
+    vtkSmartPointer<vtkPolyDataMapper> _mapper[3];
+    vtkSmartPointer<vtkPolyDataMapper> _borderMapper[3];
+    vtkSmartPointer<vtkCutter> _cutter[3];
+    vtkSmartPointer<vtkPlane> _cutPlane[3];
+#ifdef CUTPLANE_TIGHT_OUTLINE
+    vtkSmartPointer<vtkOutlineFilter> _outlineFilter[3];
+#else
+    vtkSmartPointer<vtkOutlineSource> _outlineSource[3];
+#endif
+    vtkSmartPointer<vtkGaussianSplatter> _splatter;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Cylinder.cpp
===================================================================
--- vtkvis/tags/1.8.4/Cylinder.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Cylinder.cpp	(revision 6184)
@@ -0,0 +1,64 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkCylinderSource.h>
+#include <vtkReverseSense.h>
+
+#include "Cylinder.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Cylinder::Cylinder() :
+    Shape()
+{
+}
+
+Cylinder::~Cylinder()
+{
+    TRACE("Deleting Cylinder");
+}
+
+void Cylinder::update()
+{
+    if (_cylinder == NULL) {
+	_cylinder = vtkSmartPointer<vtkCylinderSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    _pdMapper->SetInputConnection(_cylinder->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+void Cylinder::flipNormals(bool state)
+{
+    if (_cylinder == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_cylinder->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_cylinder->GetOutputPort());
+    }
+    _pdMapper->Update();
+}
Index: vtkvis/tags/1.8.4/Cylinder.h
===================================================================
--- vtkvis/tags/1.8.4/Cylinder.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Cylinder.h	(revision 6184)
@@ -0,0 +1,82 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_CYLINDER_H
+#define VTKVIS_CYLINDER_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkCylinderSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData cylinder
+ *
+ * This class creates a mesh cylinder
+ */
+class Cylinder : public Shape
+{
+public:
+    Cylinder();
+    virtual ~Cylinder();
+
+    virtual const char *getClassName() const
+    {
+        return "Cylinder";
+    }
+
+    void setCenter(double center[3])
+    {
+        if (_cylinder != NULL) {
+            _cylinder->SetCenter(center);
+	}
+    }
+
+    void setHeight(double height)
+    {
+        if (_cylinder != NULL) {
+            _cylinder->SetHeight(height);
+	}
+    }
+
+    void setRadius(double radius)
+    {
+        if (_cylinder != NULL) {
+            _cylinder->SetRadius(radius);
+	}
+    }
+
+    void setResolution(int res)
+    {
+        if (_cylinder != NULL) {
+            _cylinder->SetResolution(res);
+	}
+    }
+
+    void setCapping(bool state)
+    {
+        if (_cylinder != NULL) {
+            _cylinder->SetCapping(state ? 1 : 0);
+	}
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkCylinderSource> _cylinder;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/DataSet.cpp
===================================================================
--- vtkvis/tags/1.8.4/DataSet.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/DataSet.cpp	(revision 6184)
@@ -0,0 +1,999 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+#include <cstring>
+#include <cfloat>
+#include <cmath>
+
+#include <vtkCharArray.h>
+#include <vtkDataSetReader.h>
+#include <vtkDataSetWriter.h>
+#include <vtkPolyData.h>
+#include <vtkStructuredPoints.h>
+#include <vtkStructuredGrid.h>
+#include <vtkRectilinearGrid.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkProperty.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCell.h>
+#include <vtkLookupTable.h>
+#include <vtkExtractUnstructuredGrid.h>
+
+#include "DataSet.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+DataSet::DataSet(const std::string& name) :
+    _name(name),
+    _visible(true),
+    _opacity(1),
+    _cellSizeAverage(0)
+{
+    _cellSizeRange[0] = -1;
+    _cellSizeRange[1] = -1;
+}
+
+DataSet::~DataSet()
+{
+}
+
+/**
+ * \brief Set opacity of DataSet outline
+ *
+ * This method is used for record-keeping and opacity of the
+ * DataSet bounds outline.  The renderer controls opacity 
+ * of other related graphics objects.
+ */
+void DataSet::setOpacity(double opacity)
+{
+    _opacity = opacity;
+}
+
+/**
+ * \brief Set visibility flag in DataSet
+ *
+ * This method is used for record-keeping and visibility of the
+ * DataSet bounds outline.  The renderer controls visibility 
+ * of other related graphics objects.
+ */
+void DataSet::setVisibility(bool state)
+{
+    _visible = state;
+}
+
+/**
+ * \brief Get visibility flag in DataSet
+ *
+ * This method is used for record-keeping.  The renderer controls
+ * the visibility of related graphics objects.
+ */
+bool DataSet::getVisibility() const
+{
+    return _visible;
+}
+
+void DataSet::writeDataFile(const char *filename)
+{
+    if (_dataSet == NULL)
+        return;
+    
+    vtkSmartPointer<vtkDataSetWriter> writer = vtkSmartPointer<vtkDataSetWriter>::New();
+
+    writer->SetFileName(filename);
+    writer->SetInputData(_dataSet);
+    writer->Write();
+}
+
+/**
+ * \brief Read a VTK data file
+ */
+bool DataSet::setDataFile(const char *filename)
+{
+    vtkSmartPointer<vtkDataSetReader> reader = vtkSmartPointer<vtkDataSetReader>::New();
+
+#if defined(WANT_TRACE) && defined(DEBUG)
+    reader->DebugOn();
+#endif
+
+    reader->SetFileName(filename);
+    reader->ReadAllNormalsOn();
+    reader->ReadAllTCoordsOn();
+    reader->ReadAllScalarsOn();
+    reader->ReadAllColorScalarsOn();
+    reader->ReadAllVectorsOn();
+    reader->ReadAllTensorsOn();
+    reader->ReadAllFieldsOn();
+
+    return setData(reader);
+}
+
+/**
+ * \brief Read a VTK data file from a memory buffer
+ */
+bool DataSet::setData(char *data, int nbytes)
+{
+    vtkSmartPointer<vtkDataSetReader> reader = vtkSmartPointer<vtkDataSetReader>::New();
+    vtkSmartPointer<vtkCharArray> dataSetString = vtkSmartPointer<vtkCharArray>::New();
+
+#if defined(WANT_TRACE) && defined(DEBUG)
+    reader->DebugOn();
+    dataSetString->DebugOn();
+#endif
+
+    dataSetString->SetArray(data, nbytes, 1);
+    reader->SetInputArray(dataSetString);
+    reader->ReadFromInputStringOn();
+    reader->ReadAllNormalsOn();
+    reader->ReadAllTCoordsOn();
+    reader->ReadAllScalarsOn();
+    reader->ReadAllColorScalarsOn();
+    reader->ReadAllVectorsOn();
+    reader->ReadAllTensorsOn();
+    reader->ReadAllFieldsOn();
+
+    return setData(reader);
+}
+
+/**
+ * \brief Read dataset using supplied reader
+ */
+bool DataSet::setData(vtkDataSetReader *reader)
+{
+    TRACE("Enter");
+    // Force reading data set
+    reader->SetLookupTableName("");
+    reader->Update();
+
+    _dataSet = reader->GetOutput();
+    if (_dataSet == NULL)
+        return false;
+
+    if (vtkUnstructuredGrid::SafeDownCast(_dataSet) != NULL && !isCloud()) {
+        vtkSmartPointer<vtkExtractUnstructuredGrid> filter = vtkSmartPointer<vtkExtractUnstructuredGrid>::New();
+        filter->SetInputData(_dataSet);
+        filter->MergingOn();
+        filter->ReleaseDataFlagOn();
+        filter->Update();
+        _dataSet = filter->GetOutput();
+    }
+
+    if (_dataSet->GetPointData() != NULL &&
+        _dataSet->GetPointData()->GetScalars() != NULL &&
+        _dataSet->GetPointData()->GetScalars()->GetLookupTable() != NULL) {
+        USER_ERROR("No lookup table should be specified in VTK data sets");
+    }
+
+    setDefaultArrays();
+
+#ifdef WANT_TRACE
+    print();
+#endif
+    TRACE("Leave");
+    return true;
+}
+
+/**
+ * \brief Set DataSet from existing vtkDataSet object
+ */
+bool DataSet::setData(vtkDataSet *ds)
+{
+    _dataSet = ds;
+
+    if (_dataSet->GetPointData() != NULL &&
+        _dataSet->GetPointData()->GetScalars() != NULL &&
+        _dataSet->GetPointData()->GetScalars()->GetLookupTable() != NULL) {
+        USER_ERROR("No lookup table should be specified in VTK data sets");
+    }
+
+    setDefaultArrays();
+
+#ifdef WANT_TRACE
+    print();
+#endif
+    return true;
+}
+
+/**
+ * \brief Copy an existing vtkDataSet object
+ *
+ * Pipeline information is not copied from the supplied vtkDataSet
+ * into this DataSet, but pipeline information in the supplied 
+ * vtkDataSet is not removed
+ */
+vtkDataSet *DataSet::copyData(vtkDataSet *ds)
+{
+    if (vtkPolyData::SafeDownCast(ds) != NULL) {
+        _dataSet = vtkSmartPointer<vtkPolyData>::New();
+        _dataSet->DeepCopy(vtkPolyData::SafeDownCast(ds));
+    } else if (vtkStructuredPoints::SafeDownCast(ds) != NULL) {
+        _dataSet = vtkSmartPointer<vtkStructuredPoints>::New();
+        _dataSet->DeepCopy(vtkStructuredPoints::SafeDownCast(ds));
+    } else if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
+        _dataSet = vtkSmartPointer<vtkStructuredGrid>::New();
+        _dataSet->DeepCopy(vtkStructuredGrid::SafeDownCast(ds));
+    } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
+        _dataSet = vtkSmartPointer<vtkRectilinearGrid>::New();
+        _dataSet->DeepCopy(vtkRectilinearGrid::SafeDownCast(ds));
+    } else if (vtkUnstructuredGrid::SafeDownCast(ds) != NULL) {
+        _dataSet = vtkSmartPointer<vtkUnstructuredGrid>::New();
+        _dataSet->DeepCopy(vtkUnstructuredGrid::SafeDownCast(ds));
+    } else {
+        ERROR("Unknown data type");
+        return NULL;
+    }
+
+#ifdef WANT_TRACE
+    print();
+#endif    
+    return _dataSet;
+}
+
+/**
+ * \brief Returns the dimensionality of the AABB
+ */
+int DataSet::numDimensions() const
+{
+    double bounds[6];
+    getBounds(bounds);
+    int numDims = 0;
+    if (bounds[0] != bounds[1])
+        numDims++;
+    if (bounds[2] != bounds[3])
+        numDims++;
+    if (bounds[4] != bounds[5])
+        numDims++;
+
+    return numDims;
+}
+
+/**
+ * \brief Determines if DataSet lies in a principal axis plane
+ * and if so, returns the plane normal and offset from origin
+ */
+bool DataSet::is2D(PrincipalPlane *plane, double *offset) const
+{
+    double bounds[6];
+    getBounds(bounds);
+    if (bounds[4] == bounds[5]) {
+        // Zmin = Zmax, XY plane
+        if (plane != NULL) {
+            *plane = PLANE_XY;
+        }
+        if (offset != NULL)
+            *offset = bounds[4];
+        return true;
+    } else if (bounds[0] == bounds[1]) {
+        // Xmin = Xmax, ZY plane
+        if (plane != NULL) {
+            *plane = PLANE_ZY;
+        }
+        if (offset != NULL)
+            *offset = bounds[0];
+        return true;
+    } else if (bounds[2] == bounds[3]) {
+        // Ymin = Ymax, XZ plane
+        if (plane != NULL) {
+            *plane = PLANE_XZ;
+         }
+        if (offset != NULL)
+            *offset = bounds[2];
+        return true;
+    }
+    return false;
+}
+
+/**
+ * \brief Determines a principal plane with the
+ * largest two dimensions of the AABB
+ */
+PrincipalPlane DataSet::principalPlane() const
+{
+    double bounds[6];
+    getBounds(bounds);
+    double xlen = bounds[1] - bounds[0];
+    double ylen = bounds[3] - bounds[2];
+    double zlen = bounds[5] - bounds[4];
+    if (zlen <= xlen && zlen <= ylen) {
+        return PLANE_XY;
+    } else if (xlen <= ylen && xlen <= zlen) {
+        return PLANE_ZY;
+    } else {
+        return PLANE_XZ;
+    }
+}
+
+/**
+ * \brief Determines if mesh is a point cloud (has no cells)
+ */
+bool DataSet::isCloud() const
+{
+    vtkPolyData *pd = vtkPolyData::SafeDownCast(_dataSet);
+    if (pd) {
+        // If PolyData, is a cloud if there are no cells other than vertices
+        if (pd->GetNumberOfLines() == 0 &&
+            pd->GetNumberOfPolys() == 0 &&
+            pd->GetNumberOfStrips() == 0) {
+            return true;
+        } else {
+            // Has cells
+            return false;
+        }
+    } else {
+        return (_dataSet->GetNumberOfCells() == 0);
+    }
+}
+
+/**
+ * \brief Get the name/id of this dataset
+ */
+const std::string& DataSet::getName() const
+{
+    return _name;
+}
+
+/**
+ * \brief Get the underlying VTK DataSet object
+ */
+vtkDataSet *DataSet::getVtkDataSet()
+{
+    return _dataSet;
+}
+
+/**
+ * \brief Get the underlying VTK DataSet subclass class name
+ */
+const char *DataSet::getVtkType() const
+{
+    return _dataSet->GetClassName();
+}
+
+/**
+ * \brief Set the ative scalar array to the named field
+ */
+bool DataSet::setActiveScalars(const char *name)
+{
+    bool found = false;
+    if (_dataSet != NULL) {
+        if (!hasField(name)) {
+            ERROR("No field named %s in %s", name, getName().c_str());
+        } else {
+            if (_dataSet->GetPointData() != NULL) {
+                if (_dataSet->GetPointData()->SetActiveScalars(name) >= 0) {
+                    TRACE("Set active point data scalars for %s to %s", getName().c_str(), name);
+                    found = true;
+                }
+            }
+            if (_dataSet->GetCellData() != NULL) {
+                if (_dataSet->GetCellData()->SetActiveScalars(name) >= 0) {
+                    TRACE("Set active cell data scalars for %s to %s", getName().c_str(), name);
+                    found = true;
+                }
+            }
+        }
+    }
+#ifdef WANT_TRACE
+    if (_dataSet->GetPointData() != NULL) {
+        if (_dataSet->GetPointData()->GetScalars() != NULL) {
+            TRACE("Point data scalars for %s: %s", getName().c_str(), _dataSet->GetPointData()->GetScalars()->GetName());
+        } else {
+            TRACE("NULL point data scalars for %s", getName().c_str());
+        }
+    }
+    if (_dataSet->GetCellData() != NULL) {
+        if (_dataSet->GetCellData()->GetScalars() != NULL) {
+            TRACE("Cell data scalars for %s: %s", getName().c_str(), _dataSet->GetCellData()->GetScalars()->GetName());
+        } else {
+            TRACE("NULL cell data scalars for %s", getName().c_str());
+        }
+    }
+#endif
+    return found;
+}
+
+/**
+ * \brief Get the active scalar array field name
+ */
+const char *DataSet::getActiveScalarsName() const
+{
+    if (_dataSet != NULL) {
+         if (_dataSet->GetPointData() != NULL &&
+             _dataSet->GetPointData()->GetScalars() != NULL) {
+            return _dataSet->GetPointData()->GetScalars()->GetName();
+        }
+#ifdef DEBUG
+        TRACE("No point scalars");
+#endif
+        if (_dataSet->GetCellData() != NULL &&
+            _dataSet->GetCellData()->GetScalars() != NULL) {
+            return _dataSet->GetCellData()->GetScalars()->GetName();
+        }
+#ifdef DEBUG
+        TRACE("No cell scalars");
+#endif
+    }
+    return NULL;
+}
+
+/**
+ * \brief Get the active scalar array default attribute type
+ */
+DataSet::DataAttributeType DataSet::getActiveScalarsType() const
+{
+    if (_dataSet != NULL) {
+         if (_dataSet->GetPointData() != NULL &&
+             _dataSet->GetPointData()->GetScalars() != NULL) {
+            return POINT_DATA;
+        }
+#ifdef DEBUG
+        TRACE("No point scalars");
+#endif
+        if (_dataSet->GetCellData() != NULL &&
+            _dataSet->GetCellData()->GetScalars() != NULL) {
+            return CELL_DATA;
+        }
+#ifdef DEBUG
+        TRACE("No cell scalars");
+#endif
+    }
+    return POINT_DATA;
+}
+
+/**
+ * \brief Set the ative vector array to the named field
+ */
+bool DataSet::setActiveVectors(const char *name)
+{
+    bool found = false;
+    if (_dataSet != NULL) {
+        if (!hasField(name)) {
+            ERROR("No field named %s in %s", name, getName().c_str());
+        } else {
+            if (_dataSet->GetPointData() != NULL) {
+                if (_dataSet->GetPointData()->SetActiveVectors(name) >= 0) {
+                    TRACE("Set active point data vectors for %s to %s", getName().c_str(), name);
+                    found = true;
+                }
+            }
+            if (_dataSet->GetCellData() != NULL) {
+                if (_dataSet->GetCellData()->SetActiveVectors(name) >= 0) {
+                    TRACE("Set active cell data vectors for %s to %s", getName().c_str(), name);
+                    found = true;
+                }
+            }
+        }
+    }
+#ifdef WANT_TRACE
+    if (_dataSet->GetPointData() != NULL) {
+        if (_dataSet->GetPointData()->GetVectors() != NULL) {
+            TRACE("Point data vectors for %s: %s", getName().c_str(), _dataSet->GetPointData()->GetVectors()->GetName());
+        } else {
+            TRACE("NULL point data vectors for %s", getName().c_str());
+        }
+    }
+    if (_dataSet->GetCellData() != NULL) {
+        if (_dataSet->GetCellData()->GetVectors() != NULL) {
+            TRACE("Cell data vectors for %s: %s", getName().c_str(), _dataSet->GetCellData()->GetVectors()->GetName());
+        } else {
+            TRACE("NULL cell data vectors for %s", getName().c_str());
+        }
+    }
+#endif
+    return found;
+}
+
+/**
+ * \brief Get the active vector array default attribute type
+ */
+DataSet::DataAttributeType DataSet::getActiveVectorsType() const
+{
+    if (_dataSet != NULL) {
+         if (_dataSet->GetPointData() != NULL &&
+             _dataSet->GetPointData()->GetVectors() != NULL) {
+            return POINT_DATA;
+        }
+#ifdef DEBUG
+        TRACE("No point vectors");
+#endif
+        if (_dataSet->GetCellData() != NULL &&
+            _dataSet->GetCellData()->GetVectors() != NULL) {
+            return CELL_DATA;
+        }
+#ifdef DEBUG
+        TRACE("No cell vectors");
+#endif
+    }
+    return POINT_DATA;
+}
+
+/**
+ * \brief Get the active vector array field name
+ */
+const char *DataSet::getActiveVectorsName() const
+{
+    if (_dataSet != NULL) {
+        if (_dataSet->GetPointData() != NULL &&
+            _dataSet->GetPointData()->GetVectors() != NULL) {
+            return _dataSet->GetPointData()->GetVectors()->GetName();
+        }
+#ifdef DEBUG
+        TRACE("No point vectors");
+#endif
+        if (_dataSet->GetCellData() != NULL &&
+            _dataSet->GetCellData()->GetVectors() != NULL) {
+            return _dataSet->GetCellData()->GetVectors()->GetName();
+        }
+#ifdef DEBUG
+        TRACE("No cell vectors");
+#endif
+    }
+    return NULL;
+}
+
+void DataSet::setDefaultArrays()
+{
+    if (_dataSet->GetPointData() != NULL &&
+        _dataSet->GetPointData()->GetScalars() == NULL &&
+        _dataSet->GetPointData()->GetNumberOfArrays() > 0) {
+        for (int i = 0; i < _dataSet->GetPointData()->GetNumberOfArrays(); i++) {
+            if (_dataSet->GetPointData()->GetArray(i) != NULL &&
+                _dataSet->GetPointData()->GetArray(i)->GetNumberOfComponents() == 1) {
+                TRACE("Setting point scalars to '%s'", _dataSet->GetPointData()->GetArrayName(i));
+                _dataSet->GetPointData()->SetActiveScalars(_dataSet->GetPointData()->GetArrayName(i));
+                break;
+            }
+        }
+    }
+    if (_dataSet->GetPointData() != NULL &&
+        _dataSet->GetPointData()->GetVectors() == NULL &&
+        _dataSet->GetPointData()->GetNumberOfArrays() > 0) {
+        for (int i = 0; i < _dataSet->GetPointData()->GetNumberOfArrays(); i++) {
+            if (_dataSet->GetPointData()->GetArray(i) != NULL &&
+                _dataSet->GetPointData()->GetArray(i)->GetNumberOfComponents() == 3) {
+                TRACE("Setting point vectors to '%s'", _dataSet->GetPointData()->GetArrayName(i));
+                _dataSet->GetPointData()->SetActiveVectors(_dataSet->GetPointData()->GetArrayName(i));
+                break;
+            }
+        }
+    }
+    if (_dataSet->GetCellData() != NULL &&
+        _dataSet->GetCellData()->GetScalars() == NULL &&
+        _dataSet->GetCellData()->GetNumberOfArrays() > 0) {
+        for (int i = 0; i < _dataSet->GetCellData()->GetNumberOfArrays(); i++) {
+            if (_dataSet->GetCellData()->GetArray(i) != NULL &&
+                _dataSet->GetCellData()->GetArray(i)->GetNumberOfComponents() == 1) {
+                TRACE("Setting cell scalars to '%s'", _dataSet->GetCellData()->GetArrayName(i));
+                _dataSet->GetCellData()->SetActiveScalars(_dataSet->GetCellData()->GetArrayName(i));
+                break;
+            }
+        }
+    }
+    if (_dataSet->GetCellData() != NULL &&
+        _dataSet->GetCellData()->GetVectors() == NULL &&
+        _dataSet->GetCellData()->GetNumberOfArrays() > 0) {
+        for (int i = 0; i < _dataSet->GetCellData()->GetNumberOfArrays(); i++) {
+            if (_dataSet->GetCellData()->GetArray(i) != NULL &&
+                _dataSet->GetCellData()->GetArray(i)->GetNumberOfComponents() == 3) {
+                TRACE("Setting cell vectors to '%s'", _dataSet->GetCellData()->GetArrayName(i));
+                _dataSet->GetCellData()->SetActiveVectors(_dataSet->GetCellData()->GetArrayName(i));
+                break;
+            }
+        }
+    }
+}
+
+bool DataSet::getFieldInfo(const char *fieldName,
+                           DataAttributeType *type,
+                           int *numComponents) const
+{
+    if (_dataSet->GetPointData() != NULL &&
+        _dataSet->GetPointData()->GetAbstractArray(fieldName) != NULL) {
+        if (type != NULL)
+            *type = POINT_DATA;
+        if (numComponents != NULL)
+            *numComponents = _dataSet->GetPointData()->GetAbstractArray(fieldName)->GetNumberOfComponents();
+        return true;
+    } else if (_dataSet->GetCellData() != NULL &&
+               _dataSet->GetCellData()->GetAbstractArray(fieldName) != NULL) {
+        if (type != NULL)
+            *type = CELL_DATA;
+        if (numComponents != NULL)
+            *numComponents = _dataSet->GetCellData()->GetAbstractArray(fieldName)->GetNumberOfComponents();
+        return true;
+    } else if (_dataSet->GetFieldData() != NULL &&
+               _dataSet->GetFieldData()->GetAbstractArray(fieldName) != NULL) {
+        if (type != NULL)
+            *type = FIELD_DATA;
+        if (numComponents != NULL)
+            *numComponents = _dataSet->GetFieldData()->GetAbstractArray(fieldName)->GetNumberOfComponents();
+        return true;
+    }
+    return false;
+}
+
+bool DataSet::getFieldInfo(const char *fieldName,
+                           DataAttributeType type,
+                           int *numComponents) const
+{
+    switch (type) {
+    case POINT_DATA:
+        if (_dataSet->GetPointData() != NULL &&
+            _dataSet->GetPointData()->GetAbstractArray(fieldName) != NULL) {
+            if (numComponents != NULL)
+                *numComponents = _dataSet->GetPointData()->GetAbstractArray(fieldName)->GetNumberOfComponents();
+            return true;
+        } else
+            return false;
+        break;
+    case CELL_DATA:
+        if (_dataSet->GetCellData() != NULL &&
+            _dataSet->GetCellData()->GetAbstractArray(fieldName) != NULL) {
+            if (numComponents != NULL)
+                *numComponents = _dataSet->GetCellData()->GetAbstractArray(fieldName)->GetNumberOfComponents();
+            return true;
+        } else
+            return false;
+        break;
+    case FIELD_DATA:
+        if (_dataSet->GetFieldData() != NULL &&
+            _dataSet->GetFieldData()->GetAbstractArray(fieldName) != NULL) {
+            if (numComponents != NULL)
+                *numComponents = _dataSet->GetFieldData()->GetAbstractArray(fieldName)->GetNumberOfComponents();
+            return true;
+        } else
+            return false;
+        break;
+    default:
+        ;
+    }
+    return false;
+}
+
+/**
+ * \brief Get the list of field names in the DataSet
+ * 
+ * \param[in,out] names The field names will be appended to this list
+ * \param[in] type The DataAttributeType: e.g. POINT_DATA, CELL_DATA
+ * \param[in] numComponents Filter list by number of components, -1 means to
+ * return all fields regardless of dimension
+ */
+void DataSet::getFieldNames(std::vector<std::string>& names,
+                            DataAttributeType type, int numComponents) const
+{
+    if (_dataSet == NULL)
+        return;
+    switch (type) {
+    case POINT_DATA:
+        if (_dataSet->GetPointData() != NULL) {
+            for (int i = 0; i < _dataSet->GetPointData()->GetNumberOfArrays(); i++) {
+                if (numComponents == -1 ||
+                    (_dataSet->GetPointData()->GetAbstractArray(i) != NULL &&
+                     _dataSet->GetPointData()->GetAbstractArray(i)->GetNumberOfComponents() == numComponents)) {
+                    names.push_back(_dataSet->GetPointData()->GetArrayName(i));
+                }
+            }
+        }
+        break;
+    case CELL_DATA:
+        if (_dataSet->GetCellData() != NULL) {
+            for (int i = 0; i < _dataSet->GetCellData()->GetNumberOfArrays(); i++) {
+                if (numComponents == -1 ||
+                    (_dataSet->GetCellData()->GetAbstractArray(i) != NULL &&
+                     _dataSet->GetCellData()->GetAbstractArray(i)->GetNumberOfComponents() == numComponents)) {
+                    names.push_back(_dataSet->GetCellData()->GetArrayName(i));
+                }
+            }
+        }
+        break;
+    case FIELD_DATA:
+        if (_dataSet->GetFieldData() != NULL) {
+            for (int i = 0; i < _dataSet->GetFieldData()->GetNumberOfArrays(); i++) {
+                if (numComponents == -1 ||
+                    (_dataSet->GetFieldData()->GetAbstractArray(i) != NULL &&
+                     _dataSet->GetFieldData()->GetAbstractArray(i)->GetNumberOfComponents() == numComponents)) {
+                    names.push_back(_dataSet->GetFieldData()->GetArrayName(i));
+                }
+            }
+        }
+        break;
+    default:
+        ERROR("Unknown DataAttributeType %d", type);
+    }
+}
+
+/**
+ * \brief Get the range of scalar values in the DataSet
+ */
+void DataSet::getScalarRange(double minmax[2]) const
+{
+    if (_dataSet == NULL)
+        return;
+    if (_dataSet->GetPointData() != NULL &&
+        _dataSet->GetPointData()->GetScalars() != NULL) {
+        _dataSet->GetPointData()->GetScalars()->GetRange(minmax, -1);
+    } else if (_dataSet->GetCellData() != NULL &&
+               _dataSet->GetCellData()->GetScalars() != NULL) {
+        _dataSet->GetCellData()->GetScalars()->GetRange(minmax, -1);
+    }
+}
+
+/**
+ * \brief Get the range of a vector component in the DataSet
+ *
+ * \param[out] minmax The data range
+ * \param[in] component The field component, -1 means magnitude
+ */
+void DataSet::getVectorRange(double minmax[2], int component) const
+{
+    if (_dataSet == NULL)
+        return;
+    if (_dataSet->GetPointData() != NULL &&
+        _dataSet->GetPointData()->GetVectors() != NULL) {
+        _dataSet->GetPointData()->GetVectors()->GetRange(minmax, component);
+    } else if (_dataSet->GetCellData() != NULL &&
+               _dataSet->GetCellData()->GetVectors() != NULL) {
+        _dataSet->GetCellData()->GetVectors()->GetRange(minmax, component);
+    }
+}
+
+/**
+ * \brief Get the range of values for the named field in the DataSet
+ *
+ * \param[out] minmax The data range
+ * \param[in] fieldName The array name
+ * \param[in] type The DataAttributeType
+ * \param[in] component The field component, -1 means magnitude
+ * \return boolean indicating if field was found
+ */
+bool DataSet::getDataRange(double minmax[2], const char *fieldName,
+                           DataAttributeType type, int component) const
+{
+    if (_dataSet == NULL)
+        return false;
+    switch (type) {
+    case POINT_DATA:
+        if (_dataSet->GetPointData() != NULL &&
+            _dataSet->GetPointData()->GetArray(fieldName) != NULL) {
+            _dataSet->GetPointData()->GetArray(fieldName)->GetRange(minmax, component);
+            return true;
+        } else {
+            return false;
+        }
+        break;
+    case CELL_DATA:
+        if (_dataSet->GetCellData() != NULL &&
+            _dataSet->GetCellData()->GetArray(fieldName) != NULL) {
+            _dataSet->GetCellData()->GetArray(fieldName)->GetRange(minmax, component);
+            return true;
+        } else {
+            return false;
+        }
+        break;
+    case FIELD_DATA:
+        if (_dataSet->GetFieldData() != NULL &&
+            _dataSet->GetFieldData()->GetArray(fieldName) != NULL) {
+            _dataSet->GetFieldData()->GetArray(fieldName)->GetRange(minmax, component);
+            return true;
+        } else {
+            return false;
+        }
+        break;
+    default:
+        ERROR("Unknown DataAttributeType %d", type);
+        break;
+    }
+    return false;
+}
+
+/**
+ * \brief Get the bounds the DataSet
+ */
+void DataSet::getBounds(double bounds[6]) const
+{
+    _dataSet->GetBounds(bounds);
+}
+
+/**
+ * \brief Get the range of cell AABB diagonal lengths in the DataSet
+ */
+void DataSet::getCellSizeRange(double minmax[2], double *average)
+{
+    // Check for cached values
+    if (_cellSizeRange[0] >= 0.0) {
+        minmax[0] = _cellSizeRange[0];
+        minmax[1] = _cellSizeRange[1];
+        *average = _cellSizeAverage;
+        return;
+    }
+
+    getCellSizeRange(_dataSet, minmax, average);
+
+    // Save values in cache
+    _cellSizeRange[0] = minmax[0];
+    _cellSizeRange[1] = minmax[1];
+    _cellSizeAverage = *average;
+}
+
+/**
+ * \brief Get the range of cell AABB diagonal lengths in the DataSet
+ */
+void DataSet::getCellSizeRange(vtkDataSet *dataSet, double minmax[2], double *average)
+{
+    if (dataSet == NULL ||
+        dataSet->GetNumberOfCells() < 1) {
+        minmax[0] = 1;
+        minmax[1] = 1;
+        *average = 1;
+        return;
+    }
+
+    minmax[0] = DBL_MAX;
+    minmax[1] = -DBL_MAX;
+
+    *average = 0;
+    for (int i = 0; i < dataSet->GetNumberOfCells(); i++) {
+        double length2 = dataSet->GetCell(i)->GetLength2();
+        if (length2 < minmax[0])
+            minmax[0] = length2;
+        if (length2 > minmax[1])
+            minmax[1] = length2;
+        *average += length2;
+    }
+    if (minmax[0] == DBL_MAX)
+        minmax[0] = 1;
+    if (minmax[1] == -DBL_MAX)
+        minmax[1] = 1;
+
+    minmax[0] = sqrt(minmax[0]);
+    minmax[1] = sqrt(minmax[1]);
+    *average = sqrt(*average/((double)dataSet->GetNumberOfCells()));
+}
+
+/**
+ * \brief Get nearest data value given world coordinates x,y,z
+ *
+ * Note: no interpolation is performed on data
+ *
+ * \param[in] x World x coordinate to probe
+ * \param[in] y World y coordinate to probe
+ * \param[in] z World z coordinate to probe
+ * \param[out] value On success, contains the data value
+ * \return boolean indicating success or failure
+ */
+bool DataSet::getScalarValue(double x, double y, double z, double *value) const
+{
+    if (_dataSet == NULL)
+        return false;
+    if (_dataSet->GetPointData() == NULL ||
+        _dataSet->GetPointData()->GetScalars() == NULL) {
+        return false;
+    }
+    vtkIdType pt = _dataSet->FindPoint(x, y, z);
+    if (pt < 0)
+        return false;
+    *value = _dataSet->GetPointData()->GetScalars()->GetComponent(pt, 0);
+    return true;
+}
+
+/**
+ * \brief Get nearest vector data value given world coordinates x,y,z
+ *
+ * Note: no interpolation is performed on data
+ *
+ * \param[in] x World x coordinate to probe
+ * \param[in] y World y coordinate to probe
+ * \param[in] z World z coordinate to probe
+ * \param[out] vector On success, contains the data values
+ * \return boolean indicating success or failure
+ */
+bool DataSet::getVectorValue(double x, double y, double z, double vector[3]) const
+{
+    if (_dataSet == NULL)
+        return false;
+    if (_dataSet->GetPointData() == NULL ||
+        _dataSet->GetPointData()->GetVectors() == NULL) {
+        return false;
+    }
+    vtkIdType pt = _dataSet->FindPoint(x, y, z);
+    if (pt < 0)
+        return false;
+    assert(_dataSet->GetPointData()->GetVectors()->GetNumberOfComponents() == 3);
+    _dataSet->GetPointData()->GetVectors()->GetTuple(pt, vector);
+    return true;
+}
+
+void DataSet::print() const
+{
+    print(_dataSet);
+}
+
+void DataSet::print(vtkDataSet *ds)
+{
+    if (ds == NULL)
+        return;
+
+    TRACE("DataSet class: %s", ds->GetClassName());
+
+    TRACE("DataSet memory: %g MiB", ds->GetActualMemorySize()/1024.);
+
+    double bounds[6];
+    ds->GetBounds(bounds);
+
+    // Topology
+    TRACE("DataSet bounds: %g %g %g %g %g %g",
+          bounds[0], bounds[1],
+          bounds[2], bounds[3],
+          bounds[4], bounds[5]);
+    TRACE("Points: %d Cells: %d", ds->GetNumberOfPoints(), ds->GetNumberOfCells());
+
+    double dataRange[2];
+    if (ds->GetPointData() != NULL) {
+        TRACE("PointData arrays: %d", ds->GetPointData()->GetNumberOfArrays());
+        for (int i = 0; i < ds->GetPointData()->GetNumberOfArrays(); i++) {
+            if (ds->GetPointData()->GetArray(i) != NULL) {
+                ds->GetPointData()->GetArray(i)->GetRange(dataRange, -1);
+                TRACE("PointData[%d]: '%s' comp:%d, (%g,%g)", i,
+                      ds->GetPointData()->GetArrayName(i),
+                      ds->GetPointData()->GetAbstractArray(i)->GetNumberOfComponents(),
+                      dataRange[0], dataRange[1]);
+            } else {
+                TRACE("PointData[%d]: '%s' comp:%d", i,
+                      ds->GetPointData()->GetArrayName(i),
+                      ds->GetPointData()->GetAbstractArray(i)->GetNumberOfComponents());
+            }
+        }
+        if (ds->GetPointData()->GetScalars() != NULL) {
+            TRACE("Active point scalars: %s", ds->GetPointData()->GetScalars()->GetName());
+        }
+        if (ds->GetPointData()->GetVectors() != NULL) {
+            TRACE("Active point vectors: %s", ds->GetPointData()->GetVectors()->GetName());
+        }
+    }
+    if (ds->GetCellData() != NULL) {
+        TRACE("CellData arrays: %d", ds->GetCellData()->GetNumberOfArrays());
+        for (int i = 0; i < ds->GetCellData()->GetNumberOfArrays(); i++) {
+            if (ds->GetCellData()->GetArray(i) != NULL) {
+                ds->GetCellData()->GetArray(i)->GetRange(dataRange, -1);
+                TRACE("CellData[%d]: '%s' comp:%d, (%g,%g)", i,
+                      ds->GetCellData()->GetArrayName(i),
+                      ds->GetCellData()->GetAbstractArray(i)->GetNumberOfComponents(),
+                      dataRange[0], dataRange[1]);
+            } else {
+                TRACE("CellData[%d]: '%s' comp:%d", i,
+                      ds->GetCellData()->GetArrayName(i),
+                      ds->GetCellData()->GetAbstractArray(i)->GetNumberOfComponents());
+            }
+        }
+        if (ds->GetCellData()->GetScalars() != NULL) {
+            TRACE("Active cell scalars: %s", ds->GetCellData()->GetScalars()->GetName());
+        }
+        if (ds->GetCellData()->GetVectors() != NULL) {
+            TRACE("Active cell vectors: %s", ds->GetCellData()->GetVectors()->GetName());
+        }
+    }
+    if (ds->GetFieldData() != NULL) {
+        TRACE("FieldData arrays: %d", ds->GetFieldData()->GetNumberOfArrays());
+        for (int i = 0; i < ds->GetFieldData()->GetNumberOfArrays(); i++) {
+            if (ds->GetFieldData()->GetArray(i) != NULL) {
+                ds->GetFieldData()->GetArray(i)->GetRange(dataRange, -1);
+                TRACE("FieldData[%d]: '%s' comp:%d, tuples:%d (%g,%g)", i,
+                      ds->GetFieldData()->GetArrayName(i),
+                      ds->GetFieldData()->GetAbstractArray(i)->GetNumberOfComponents(),
+                      ds->GetFieldData()->GetAbstractArray(i)->GetNumberOfTuples(),
+                      dataRange[0], dataRange[1]);
+            } else {
+                TRACE("FieldData[%d]: '%s' comp:%d, tuples:%d", i,
+                      ds->GetFieldData()->GetArrayName(i),
+                      ds->GetFieldData()->GetAbstractArray(i)->GetNumberOfComponents(),
+                      ds->GetFieldData()->GetAbstractArray(i)->GetNumberOfTuples());
+            }
+        }
+    }
+}
+
Index: vtkvis/tags/1.8.4/DataSet.h
===================================================================
--- vtkvis/tags/1.8.4/DataSet.h	(revision 6184)
+++ vtkvis/tags/1.8.4/DataSet.h	(revision 6184)
@@ -0,0 +1,159 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_DATASET_H
+#define VTKVIS_DATASET_H
+
+#include <vtkSmartPointer.h>
+#include <vtkDataSet.h>
+#include <vtkDataSetReader.h>
+#include <vtkAlgorithmOutput.h>
+#include <vtkTrivialProducer.h>
+
+#include <string>
+#include <vector>
+
+#include "Types.h"
+#include "Trace.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK DataSet wrapper
+ */
+class DataSet {
+public:
+    /** 
+     * These enum values intentionally match vtkDataObject::FieldAssociations
+     * and vtkDataObject::AttributeTypes
+     */
+    enum DataAttributeType {
+        POINT_DATA,
+        CELL_DATA,
+        FIELD_DATA
+    };
+    DataSet(const std::string& name);
+    virtual ~DataSet();
+
+    void writeDataFile(const char *filename);
+
+    bool setDataFile(const char *filename);
+
+    bool setData(char *data, int nbytes);
+
+    bool setData(vtkDataSetReader *reader);
+
+    bool setData(vtkDataSet *ds);
+
+    vtkDataSet *copyData(vtkDataSet *ds);
+
+    int numDimensions() const;
+
+    bool is2D(PrincipalPlane *plane = NULL, double *offset = NULL) const;
+
+    PrincipalPlane principalPlane() const;
+
+    bool isCloud() const;
+
+    const std::string& getName() const;
+
+    vtkDataSet *getVtkDataSet();
+
+    vtkAlgorithmOutput *getProducerPort()
+    {
+        if (_producer == NULL) {
+            _producer = vtkSmartPointer<vtkTrivialProducer>::New();
+            _producer->SetOutput(_dataSet);
+        }
+        return _producer->GetOutputPort();
+    }
+
+    const char *getVtkType() const;
+
+    bool setActiveScalars(const char *name);
+
+    const char *getActiveScalarsName() const;
+
+    DataAttributeType getActiveScalarsType() const;
+
+    bool setActiveVectors(const char *name);
+
+    const char *getActiveVectorsName() const;
+
+    DataAttributeType getActiveVectorsType() const;
+
+    bool hasField(const char *fieldName) const
+    {
+        return getFieldInfo(fieldName, NULL, NULL);
+    }
+
+    bool hasField(const char *fieldName, DataAttributeType type) const
+    {
+        return getFieldInfo(fieldName, type, NULL);
+    }
+
+    bool getFieldInfo(const char *fieldName, DataAttributeType *type, int *numComponents) const;
+
+    bool getFieldInfo(const char *fieldName, DataAttributeType type, int *numComponents) const;
+
+    void getFieldNames(std::vector<std::string>& names,
+                       DataAttributeType type, int numComponents = -1) const;
+
+    bool getDataRange(double minmax[2], const char *fieldName,
+                      DataAttributeType type, int component = -1) const;
+
+    void getScalarRange(double minmax[2]) const;
+
+    void getVectorRange(double minmax[2], int component = -1) const;
+
+    void getBounds(double bounds[6]) const;
+
+    void getCellSizeRange(double minmax[2], double *average);
+
+    bool getScalarValue(double x, double y, double z, double *value) const;
+
+    bool getVectorValue(double x, double y, double z, double vector[3]) const;
+
+    void setOpacity(double opacity);
+
+    /**
+     * \brief Get the opacity setting for the DataSet
+     *
+     * This method is used for record-keeping.  The renderer controls
+     * the visibility of related graphics objects.
+     */
+    inline double getOpacity()
+    {
+        return _opacity;
+    }
+
+    void setVisibility(bool state);
+
+    bool getVisibility() const;
+
+    static void getCellSizeRange(vtkDataSet *dataSet, double minmax[2], double *average);
+
+    static void print(vtkDataSet *ds);
+
+private:
+    DataSet();
+
+    void setDefaultArrays();
+    void print() const;
+
+    std::string _name;
+    vtkSmartPointer<vtkDataSet> _dataSet;
+    vtkSmartPointer<vtkTrivialProducer> _producer;
+    bool _visible;
+    double _opacity;
+    double _cellSizeRange[2];
+    double _cellSizeAverage;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Disk.cpp
===================================================================
--- vtkvis/tags/1.8.4/Disk.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Disk.cpp	(revision 6184)
@@ -0,0 +1,68 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkDiskSource.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkReverseSense.h>
+
+#include "Disk.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Disk::Disk() :
+    Shape()
+{
+}
+
+Disk::~Disk()
+{
+    TRACE("Deleting Disk");
+}
+
+void Disk::update()
+{
+    if (_disk == NULL) {
+	_disk = vtkSmartPointer<vtkDiskSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+    normalFilter->SetInputConnection(_disk->GetOutputPort());
+
+    _pdMapper->SetInputConnection(normalFilter->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+void Disk::flipNormals(bool state)
+{
+    if (_disk == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_disk->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_disk->GetOutputPort());
+    }
+    _pdMapper->Update();
+}
Index: vtkvis/tags/1.8.4/Disk.h
===================================================================
--- vtkvis/tags/1.8.4/Disk.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Disk.h	(revision 6184)
@@ -0,0 +1,63 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_DISK_H
+#define VTKVIS_DISK_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkDiskSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData disk
+ *
+ * This class creates a mesh disk
+ */
+class Disk : public Shape
+{
+public:
+    Disk();
+    virtual ~Disk();
+
+    virtual const char *getClassName() const
+    {
+        return "Disk";
+    }
+
+    void setRadii(double inner, double outer)
+    {
+        if (_disk != NULL) {
+            _disk->SetInnerRadius(inner);
+	    _disk->SetOuterRadius(outer);
+	}
+    }
+
+    void setResolution(int radialRes, int circumRes)
+    {
+        if (_disk != NULL) {
+            _disk->SetRadialResolution(radialRes);
+	    _disk->SetCircumferentialResolution(circumRes);
+	}
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkDiskSource> _disk;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Doxyfile.in
===================================================================
--- vtkvis/tags/1.8.4/Doxyfile.in	(revision 6184)
+++ vtkvis/tags/1.8.4/Doxyfile.in	(revision 6184)
@@ -0,0 +1,1519 @@
+# Doxyfile 1.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "HUBzero VTK Render Server"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = @srcdir@
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = @srcdir@/vtkRpAxisActor.h @srcdir@/vtkRpAxisActor.cpp @srcdir@/vtkRpAxisFollower.h @srcdir@/vtkRpAxisFollower.cpp
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# If the HTML_TIMESTAMP tag is set to YES then the generated HTML
+# documentation will contain the timesstamp.
+
+HTML_TIMESTAMP         = NO
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP)
+# there is already a search function so this one should typically
+# be disabled.
+
+SEARCHENGINE           = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
Index: vtkvis/tags/1.8.4/Glyphs.cpp
===================================================================
--- vtkvis/tags/1.8.4/Glyphs.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Glyphs.cpp	(revision 6184)
@@ -0,0 +1,912 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cstring>
+#include <cfloat>
+
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCellDataToPointData.h>
+#include <vtkProp.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkGlyph3D.h>
+#include <vtkLineSource.h>
+#include <vtkArrowSource.h>
+#include <vtkConeSource.h>
+#include <vtkCubeSource.h>
+#include <vtkCylinderSource.h>
+#include <vtkPlatonicSolidSource.h>
+#include <vtkPointSource.h>
+#include <vtkSphereSource.h>
+#include <vtkTransform.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkTransformPolyDataFilter.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkPolyData.h>
+#include <vtkUnstructuredGrid.h>
+
+#include "Glyphs.h"
+#include "Renderer.h"
+#include "Math.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Glyphs::Glyphs(GlyphShape shape) :
+    GraphicsObject(),
+    _glyphShape(shape),
+    _scalingMode(SCALE_BY_VECTOR_MAGNITUDE),
+    _dataScale(1.0),
+    _scaleFactor(1.0),
+    _normalizeScale(true),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR)
+{
+    _faceCulling = true;
+    _scalingFieldRange[0] = DBL_MAX;
+    _scalingFieldRange[1] = -DBL_MAX;
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+Glyphs::~Glyphs()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Glyphs for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Glyphs with NULL DataSet");
+#endif
+}
+
+void Glyphs::setDataSet(DataSet *dataSet,
+                        Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Set the shape of the glyphs
+ */
+void Glyphs::setGlyphShape(GlyphShape shape)
+{
+    _glyphShape = shape;
+
+    // Note: using vtkTransformPolyDataFilter instead of the vtkGlyph3D's 
+    // SourceTransform because of a bug: vtkGlyph3D doesn't transform normals
+    // by the SourceTransform, so the lighting would be wrong
+
+    bool needsNormals = false;
+    double featureAngle = 90.;
+
+    switch (_glyphShape) {
+    case LINE:
+        // Length of 1, centered at origin
+        _glyphSource = vtkSmartPointer<vtkLineSource>::New();
+        break;
+    case ARROW: {
+        // Height of 1, Tip radius .1, tip length .35, shaft radius .03
+        _glyphSource = vtkSmartPointer<vtkArrowSource>::New();
+        vtkSmartPointer<vtkArrowSource> arrow = vtkArrowSource::SafeDownCast(_glyphSource);
+        arrow->SetTipResolution(8);
+        arrow->SetShaftResolution(8);
+        needsNormals = true;
+    }
+        break;
+    case CONE: {
+        // base length of 1, height 1, centered at origin
+        _glyphSource = vtkSmartPointer<vtkConeSource>::New();
+        vtkSmartPointer<vtkConeSource> cone = vtkConeSource::SafeDownCast(_glyphSource);
+        cone->SetResolution(8);
+        needsNormals = true;
+    }
+        break;
+    case CUBE:
+        // Sides of length 1
+        _glyphSource = vtkSmartPointer<vtkCubeSource>::New();
+        break;
+    case CYLINDER: {
+        // base length of 1, height 1, centered at origin
+        vtkSmartPointer<vtkCylinderSource> csource = vtkSmartPointer<vtkCylinderSource>::New();
+        csource->SetResolution(8);
+        _glyphSource = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+        _glyphSource->SetInputConnection(csource->GetOutputPort());
+        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+        trans->RotateZ(-90.0);
+        vtkTransformPolyDataFilter::SafeDownCast(_glyphSource)->SetTransform(trans);
+      }
+        break;
+    case DODECAHEDRON:
+        // Radius of 1
+        _glyphSource = vtkSmartPointer<vtkPlatonicSolidSource>::New();
+        vtkPlatonicSolidSource::SafeDownCast(_glyphSource)->SetSolidTypeToDodecahedron();
+        needsNormals = true;
+        featureAngle = 30.;
+        break;
+    case ICOSAHEDRON:
+        // Radius of 1
+        _glyphSource = vtkSmartPointer<vtkPlatonicSolidSource>::New();
+        vtkPlatonicSolidSource::SafeDownCast(_glyphSource)->SetSolidTypeToIcosahedron();
+        needsNormals = true;
+        featureAngle = 30.;
+        break;
+    case OCTAHEDRON:
+        // Radius of 1
+        _glyphSource = vtkSmartPointer<vtkPlatonicSolidSource>::New();
+        vtkPlatonicSolidSource::SafeDownCast(_glyphSource)->SetSolidTypeToOctahedron();
+        needsNormals = true;
+        featureAngle = 30.;
+        break;
+    case POINT:
+        _glyphSource = vtkSmartPointer<vtkPointSource>::New();
+        vtkPointSource::SafeDownCast(_glyphSource)->SetNumberOfPoints(1);
+        vtkPointSource::SafeDownCast(_glyphSource)->SetCenter(0, 0, 0);
+        vtkPointSource::SafeDownCast(_glyphSource)->SetRadius(0);
+        vtkPointSource::SafeDownCast(_glyphSource)->SetDistributionToUniform();
+        break;
+    case SPHERE: {
+        // Default radius 0.5
+        _glyphSource = vtkSmartPointer<vtkSphereSource>::New();
+        vtkSmartPointer<vtkSphereSource> sphere = vtkSphereSource::SafeDownCast(_glyphSource);
+        sphere->SetThetaResolution(14);
+        sphere->SetPhiResolution(14);
+    }
+        break;
+    case TETRAHEDRON:
+        // Radius of 1
+        // FIXME: need to rotate inital orientation
+        _glyphSource = vtkSmartPointer<vtkPlatonicSolidSource>::New();
+        vtkPlatonicSolidSource::SafeDownCast(_glyphSource)->SetSolidTypeToTetrahedron();
+        needsNormals = true;
+        featureAngle = 30.;
+        break;
+    default:
+        ERROR("Unknown glyph shape: %d", _glyphShape);
+        return;
+    }
+
+    if (_glyphShape == ICOSAHEDRON ||
+        _glyphShape == TETRAHEDRON) {
+        // These shapes are created with front faces pointing inside
+        setCullFace(CULL_FRONT);
+    } else {
+        setCullFace(CULL_BACK);
+    }
+
+    if (needsNormals && _glyphMapper != NULL) {
+        vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+        normalFilter->SetFeatureAngle(featureAngle);
+        normalFilter->SetInputConnection(_glyphSource->GetOutputPort());
+        _glyphMapper->SetSourceConnection(normalFilter->GetOutputPort());
+    } else if (_glyphMapper != NULL) {
+        _glyphMapper->SetSourceConnection(_glyphSource->GetOutputPort());
+    }
+}
+
+void Glyphs::setQuality(double quality)
+{
+    if (quality > 10.0)
+        quality = 10.0;
+
+    switch (_glyphShape) {
+    case ARROW: {
+        vtkSmartPointer<vtkArrowSource> arrow = vtkArrowSource::SafeDownCast(_glyphSource);
+        int res = (int)(quality * 8.);
+        if (res < 3) res = 3;
+        arrow->SetTipResolution(res);
+        arrow->SetShaftResolution(res);
+    }
+        break;
+    case CONE: {
+        vtkSmartPointer<vtkConeSource> cone = vtkConeSource::SafeDownCast(_glyphSource);
+        int res = (int)(quality * 8.);
+        if (res < 3) res = 3;
+        cone->SetResolution(res);
+    }
+        break;
+    case CYLINDER: {
+        vtkSmartPointer<vtkCylinderSource> csource = 
+            vtkCylinderSource::SafeDownCast(_glyphSource->GetInputAlgorithm(0, 0));
+        if (csource == NULL) {
+            ERROR("Can't get cylinder glyph source");
+            return;
+        }
+        int res = (int)(quality * 8.);
+        if (res < 3) res = 3;
+        csource->SetResolution(res);
+    }
+        break;
+    case SPHERE: {
+        vtkSmartPointer<vtkSphereSource> sphere = vtkSphereSource::SafeDownCast(_glyphSource);
+        int thetaRes = (int)(quality * 14.);
+        int phiRes = thetaRes;
+        if (thetaRes < 4) thetaRes = 4;
+        if (phiRes < 3) phiRes = 3;
+
+        sphere->SetThetaResolution(thetaRes);
+        sphere->SetPhiResolution(phiRes);
+    }
+        break;
+    default:
+        break;
+    }
+    if (_glyphMapper != NULL) {
+        _glyphMapper->Modified();
+        _glyphMapper->Update();
+    }
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void Glyphs::update()
+{
+    if (_dataSet == NULL) {
+        return;
+    }
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    if (_glyphMapper == NULL) {
+        _glyphMapper = vtkSmartPointer<vtkGlyph3DMapper>::New();
+        _glyphMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        // If there are color scalars, use them without lookup table (if scalar visibility is on)
+        _glyphMapper->SetColorModeToDefault();
+        _glyphMapper->ScalarVisibilityOn();
+    }
+
+    initProp();
+
+    setGlyphShape(_glyphShape);
+
+    vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
+
+    if (ds->GetPointData() == NULL ||
+        ds->GetPointData()->GetScalars() == NULL) {
+        if (ds->GetCellData() != NULL &&
+            ds->GetCellData()->GetScalars() != NULL) {
+            TRACE("Generating point data scalars from cell data for: %s", _dataSet->getName().c_str());
+            cellToPtData = 
+                vtkSmartPointer<vtkCellDataToPointData>::New();
+            cellToPtData->SetInputData(ds);
+            //cellToPtData->PassCellDataOn();
+            cellToPtData->Update();
+            ds = cellToPtData->GetOutput();
+        } else {
+            TRACE("No scalar data in dataset %s", _dataSet->getName().c_str());
+        }
+    }
+
+    _glyphMapper->SetInputData(ds);
+
+    if (ds->GetPointData()->GetVectors() != NULL) {
+        TRACE("Setting scale mode to vector magnitude");
+        setScalingMode(SCALE_BY_VECTOR_MAGNITUDE);
+    } else {
+        TRACE("Setting scale mode to scalar");
+        setScalingMode(SCALE_BY_SCALAR);
+    }
+
+    if (_dataSet->isCloud()) {
+        PrincipalPlane plane;
+        double offset;
+        if (_dataSet->is2D(&plane, &offset)) {
+            // 2D cloud
+            vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+            if (plane == PLANE_ZY) {
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->RotateWXYZ(90, 0, 1, 0);
+                if (offset != 0.0) {
+                    trans->Translate(-offset, 0, 0);
+                }
+                mesher->SetTransform(trans);
+            } else if (plane == PLANE_XZ) {
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->RotateWXYZ(-90, 1, 0, 0);
+                if (offset != 0.0) {
+                    trans->Translate(0, -offset, 0);
+                }
+                mesher->SetTransform(trans);
+            } else if (offset != 0.0) {
+                // XY with Z offset
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->Translate(0, 0, -offset);
+                mesher->SetTransform(trans);
+            }
+            mesher->SetInputData(ds);
+            mesher->Update();
+            vtkPolyData *pd = mesher->GetOutput();
+            if (pd->GetNumberOfPolys() == 0) {
+                // Meshing failed, fall back to scale based on bounds
+                double bounds[6];
+                _dataSet->getBounds(bounds);
+                double xlen = bounds[1] - bounds[0];
+                double ylen = bounds[3] - bounds[2];
+                double zlen = bounds[5] - bounds[4];
+                double max = max3(xlen, ylen, zlen);
+                _dataScale = max / 64.0;
+            } else {
+                double cellSizeRange[2];
+                double avgSize;
+                DataSet::getCellSizeRange(pd, cellSizeRange, &avgSize);
+                _dataScale = avgSize * 2.0;
+            }
+        } else {
+            // 3D cloud
+            vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+            mesher->SetInputData(ds);
+            mesher->Update();
+            vtkUnstructuredGrid *ugrid = mesher->GetOutput();
+            if (ugrid->GetNumberOfCells() == 0) {
+                // Meshing failed, fall back to scale based on bounds
+                double bounds[6];
+                _dataSet->getBounds(bounds);
+                double xlen = bounds[1] - bounds[0];
+                double ylen = bounds[3] - bounds[2];
+                double zlen = bounds[5] - bounds[4];
+                double max = max3(xlen, ylen, zlen);
+                _dataScale = max / 64.0;
+            } else {
+                double cellSizeRange[2];
+                double avgSize;
+                DataSet::getCellSizeRange(ugrid, cellSizeRange, &avgSize);
+                _dataScale = avgSize * 2.0;
+            }
+        }
+    } else {
+        double cellSizeRange[2];
+        double avgSize;
+        _dataSet->getCellSizeRange(cellSizeRange, &avgSize);
+        _dataScale = avgSize * 2.0;
+    }
+
+    TRACE("Data scale factor: %g", _dataScale);
+
+    // Normalize sizes to [0,1] * ScaleFactor
+    _glyphMapper->SetClamping(_normalizeScale ? 1 : 0);
+    if (_normalizeScale) {
+        _glyphMapper->SetScaleFactor(_scaleFactor * _dataScale);
+        TRACE("Setting scale factor: %g", _scaleFactor * _dataScale);
+    } else {
+        _glyphMapper->SetScaleFactor(_scaleFactor);
+        TRACE("Setting scale factor: %g", _scaleFactor);
+    }
+    _glyphMapper->ScalingOn();
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+    }
+
+    if (ds->GetPointData()->GetScalars() == NULL) {
+        TRACE("Setting color mode to vector magnitude");
+        setColorMode(COLOR_BY_VECTOR_MAGNITUDE);
+    } else {
+        TRACE("Setting color mode to scalar");
+        setColorMode(COLOR_BY_SCALAR);
+    }
+
+    getActor()->SetMapper(_glyphMapper);
+    _glyphMapper->Update();
+}
+
+/** 
+ * \brief Control if field data range is normalized to [0,1] before
+ * applying scale factor
+ */
+void Glyphs::setNormalizeScale(bool normalize)
+{
+    if (_normalizeScale != normalize) {
+        _normalizeScale = normalize;
+        if (_glyphMapper != NULL) {
+            _glyphMapper->SetClamping(_normalizeScale ? 1 : 0);
+            if (_normalizeScale) {
+                _glyphMapper->SetScaleFactor(_scaleFactor * _dataScale);
+                TRACE("Setting scale factor: %g", _scaleFactor * _dataScale);
+            } else {
+                _glyphMapper->SetScaleFactor(_scaleFactor);
+                TRACE("Setting scale factor: %g", _scaleFactor);
+            }
+        }
+    }
+}
+
+/**
+ * \brief Turn on/off orienting glyphs from a vector field
+ */
+void Glyphs::setOrientMode(bool mode, const char *name)
+{
+    if (_glyphMapper != NULL) {
+        _glyphMapper->SetOrient(mode ? 1 : 0);
+        if (name != NULL && strlen(name) > 0) {
+            _glyphMapper->SetOrientationArray(name);
+        } else {
+            _glyphMapper->SetOrientationArray(vtkDataSetAttributes::VECTORS);
+        }
+    }
+}
+
+/**
+ * \brief Control how glyphs are scaled
+ */
+void Glyphs::setScalingMode(ScalingMode mode, const char *name, double range[2])
+{
+    _scalingMode = mode;
+
+    if (_dataSet == NULL || _glyphMapper == NULL)
+        return;
+
+    if (name != NULL && strlen(name) > 0) {
+        if (!_dataSet->hasField(name, DataSet::POINT_DATA)) {
+            ERROR("Field not found: %s", name);
+            return;
+        }
+        _scalingFieldName = name;
+    } else
+        _scalingFieldName.clear();
+    if (range == NULL) {
+        _scalingFieldRange[0] = DBL_MAX;
+        _scalingFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_scalingFieldRange, range, sizeof(double)*2);
+    }
+
+    if (name != NULL && strlen(name) > 0) {
+        _glyphMapper->SetScaleArray(name);
+    } else {
+        if (mode == SCALE_BY_SCALAR) {
+            _glyphMapper->SetScaleArray(vtkDataSetAttributes::SCALARS);
+        } else {
+            _glyphMapper->SetScaleArray(vtkDataSetAttributes::VECTORS);
+        }
+    }
+
+    if (range != NULL) {
+        TRACE("Setting size range to: %g,%g", range[0], range[1]);
+        _glyphMapper->SetRange(range);
+    } else if (name != NULL && strlen(name) > 0) {
+        double r[2];
+        DataSet::DataAttributeType type = DataSet::POINT_DATA;
+        int comp = -1;
+
+        if (_renderer->getUseCumulativeRange()) {
+            int numComponents;
+            if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                ERROR("Field not found: %s, type: %d", name, type);
+                return;
+            } else if (mode == SCALE_BY_VECTOR_COMPONENTS && numComponents < 3) {
+                ERROR("Field %s needs 3 components but has only %d components",
+                      name, numComponents);
+                return;
+            }
+            if (mode == SCALE_BY_VECTOR_COMPONENTS) {
+                double tmpR[2];
+                _renderer->getCumulativeDataRange(tmpR, name, type, numComponents, 0);
+                r[0] = tmpR[0];
+                r[1] = tmpR[1];
+                _renderer->getCumulativeDataRange(tmpR, name, type, numComponents, 1);
+                r[0] = min2(r[0], tmpR[0]);
+                r[1] = max2(r[1], tmpR[1]);
+                _renderer->getCumulativeDataRange(tmpR, name, type, numComponents, 2);
+                r[0] = min2(r[0], tmpR[0]);
+                r[1] = max2(r[1], tmpR[1]);
+            } else {
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            }
+        } else {
+            if (mode == SCALE_BY_VECTOR_COMPONENTS) {
+                double tmpR[2];
+                _dataSet->getDataRange(tmpR, name, type, 0);
+                r[0] = tmpR[0];
+                r[1] = tmpR[1];
+                _dataSet->getDataRange(tmpR, name, type, 1);
+                r[0] = min2(r[0], tmpR[0]);
+                r[1] = max2(r[1], tmpR[1]);
+                _dataSet->getDataRange(tmpR, name, type, 2);
+                r[0] = min2(r[0], tmpR[0]);
+                r[1] = max2(r[1], tmpR[1]);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+        }
+        TRACE("Setting size range to: %g,%g", r[0], r[1]);
+        _glyphMapper->SetRange(r);
+    } else {
+        switch (mode) {
+        case SCALE_BY_SCALAR:
+            TRACE("Setting size range to: %g,%g", _dataRange[0], _dataRange[1]);
+            _glyphMapper->SetRange(_dataRange);
+            break;
+        case SCALE_BY_VECTOR_MAGNITUDE:
+            TRACE("Setting size range to: %g,%g", _vectorMagnitudeRange[0], _vectorMagnitudeRange[1]);
+            _glyphMapper->SetRange(_vectorMagnitudeRange);
+            break;
+        case SCALE_BY_VECTOR_COMPONENTS: {
+            double sizeRange[2];
+            sizeRange[0] = _vectorComponentRange[0][0];
+            sizeRange[1] = _vectorComponentRange[0][1];
+            sizeRange[0] = min2(sizeRange[0], _vectorComponentRange[1][0]);
+            sizeRange[1] = max2(sizeRange[1], _vectorComponentRange[1][1]);
+            sizeRange[0] = min2(sizeRange[0], _vectorComponentRange[2][0]);
+            sizeRange[1] = max2(sizeRange[1], _vectorComponentRange[2][1]);
+            TRACE("Setting size range to: %g,%g", sizeRange[0], sizeRange[1]);
+            _glyphMapper->SetRange(sizeRange);
+        }
+            break;
+        case SCALING_OFF:
+        default:
+            ;
+        }
+    }
+
+    switch (mode) {
+    case SCALE_BY_SCALAR:
+    case SCALE_BY_VECTOR_MAGNITUDE:
+        _glyphMapper->SetScaleModeToScaleByMagnitude();
+        break;
+    case SCALE_BY_VECTOR_COMPONENTS:
+        _glyphMapper->SetScaleModeToScaleByVectorComponents();
+        break;
+    case SCALING_OFF:
+    default:
+        _glyphMapper->SetScaleModeToNoDataScaling();
+    }
+}
+
+void Glyphs::setScalingMode(ScalingMode mode)
+{
+    setScalingMode(mode, NULL, NULL);
+}
+
+void Glyphs::setColorMode(ColorMode mode,
+                          const char *name, double range[2])
+{
+    _colorMode = mode;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _glyphMapper == NULL)
+        return;
+
+    if (name != NULL && strlen(name) > 0) {
+        _glyphMapper->SetScalarModeToUsePointFieldData();
+        _glyphMapper->SelectColorArray(name);
+    } else {
+        _glyphMapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            DataSet::DataAttributeType type = DataSet::POINT_DATA;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, &type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            TRACE("Setting lut range to: %g,%g", r[0], r[1]);
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _glyphMapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _glyphMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _glyphMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _glyphMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _glyphMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _glyphMapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+void Glyphs::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, NULL, NULL);
+        break;
+    }
+}
+
+/**
+ * \brief Turn on/off orienting glyphs from a vector field
+ */
+void Glyphs::setOrient(bool state)
+{
+    if (_glyphMapper != NULL) {
+        _glyphMapper->SetOrient(state ? 1 : 0);
+    }
+}
+
+/**
+ * \brief Controls relative scaling of glyphs
+ */
+void Glyphs::setScaleFactor(double scale)
+{
+    _scaleFactor = scale;
+    if (_glyphMapper != NULL) {
+        if (_normalizeScale) {
+            _glyphMapper->SetScaleFactor(_scaleFactor * _dataScale);
+            TRACE("Setting scale factor: %g", _scaleFactor * _dataScale);
+        } else {
+            _glyphMapper->SetScaleFactor(_scaleFactor);
+            TRACE("Setting scale factor: %g", _scaleFactor);
+        }
+    }
+}
+
+void Glyphs::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                         _dataSet->getActiveVectorsName(),
+                                         3);
+        for (int i = 0; i < 3; i++) {
+            renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                             _dataSet->getActiveVectorsName(),
+                                             3, i);
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+
+    // Need to update color map ranges and/or active vector field
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldName.c_str(), rangePtr);
+
+    rangePtr = _scalingFieldRange;
+    if (_scalingFieldRange[0] > _scalingFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setScalingMode(_scalingMode, _scalingFieldName.c_str(), rangePtr);
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void Glyphs::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Glyphs::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_glyphMapper != NULL) {
+            _glyphMapper->UseLookupTableScalarRangeOn();
+            _glyphMapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+        break;
+    }
+}
+
+/**
+ * \brief Limit the number of glyphs displayed
+ *
+ * The choice of glyphs to display can be based on sampling every
+ * n-th point (ratio) or by random sample
+ *
+ * \param max Maximum number of glyphs to display, negative means display all
+ * \param random Flag to enable/disable random sampling
+ * \param offset If random is false, this controls the first sample point
+ * \param ratio If random is false, this ratio controls every n-th point sampling
+ */
+void Glyphs::setMaximumNumberOfGlyphs(int max, bool random, int offset, int ratio)
+{
+    if (_dataSet == NULL || _glyphMapper == NULL)
+        return;
+
+    if (max < 0) {
+        if (_maskPoints != NULL) {
+            _glyphMapper->SetInputData(_dataSet->getVtkDataSet());
+            _maskPoints = NULL;
+        }
+    } else {
+        if (_maskPoints == NULL) {
+            _maskPoints = vtkSmartPointer<vtkMaskPoints>::New();
+        }
+        _maskPoints->SetInputData(_dataSet->getVtkDataSet());
+        _maskPoints->SetMaximumNumberOfPoints(max);
+        _maskPoints->SetOffset(offset);
+        _maskPoints->SetOnRatio(ratio);
+        _maskPoints->SetRandomMode((random ? 1 : 0));
+        _maskPoints->GenerateVerticesOff();
+        _glyphMapper->SetInputConnection(_maskPoints->GetOutputPort());
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Glyphs::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_glyphMapper != NULL) {
+        _glyphMapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Glyphs.h
===================================================================
--- vtkvis/tags/1.8.4/Glyphs.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Glyphs.h	(revision 6184)
@@ -0,0 +1,147 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_GLYPHS_H
+#define VTKVIS_GLYPHS_H
+
+#include <vector>
+
+#include <vtkSmartPointer.h>
+#include <vtkProp.h>
+#include <vtkActor.h>
+#include <vtkVersion.h>
+#include <vtkGlyph3DMapper.h>
+#include <vtkMaskPoints.h>
+#include <vtkLookupTable.h>
+#include <vtkPlaneCollection.h>
+
+#include "GraphicsObject.h"
+#include "ColorMap.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Oriented and scaled 3D glyph shapes
+ *
+ * The DataSet must have vectors and/or scalars
+ */
+class Glyphs : public GraphicsObject {
+public:
+    enum GlyphShape {
+        ARROW,
+        CONE,
+        CUBE,
+        CYLINDER,
+        DODECAHEDRON,
+        ICOSAHEDRON,
+        LINE,
+        OCTAHEDRON,
+        POINT,
+        SPHERE,
+        TETRAHEDRON
+    };
+    enum ScalingMode {
+        SCALE_BY_SCALAR,
+        SCALE_BY_VECTOR_MAGNITUDE,
+        SCALE_BY_VECTOR_COMPONENTS,
+        SCALING_OFF
+    };
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+
+    Glyphs(GlyphShape shape);
+    virtual ~Glyphs();
+
+    virtual const char *getClassName() const
+    {
+        return "Glyphs";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setQuality(double quality);
+
+    void setOrient(bool state);
+
+    void setOrientMode(bool mode, const char *name);
+
+    void setScalingMode(ScalingMode mode, const char *name, double range[2]);
+
+    void setColorMode(ColorMode mode, const char *name, double range[2] = NULL);
+
+    void setScalingMode(ScalingMode mode);
+
+    void setNormalizeScale(bool normalize);
+
+    void setColorMode(ColorMode mode);
+
+    void setGlyphShape(GlyphShape shape);
+
+    void setScaleFactor(double scale);
+
+    void setMaximumNumberOfGlyphs(int max, bool random = true,
+                                  int offset = 0, int ratio = 1);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    Glyphs();
+    virtual void update();
+    static inline double min2(double a, double b)
+    {
+        return ((a < b) ? a : b);
+    }
+    static inline double max2(double a, double b)
+    {
+        return ((a > b) ? a : b);
+    }
+
+    GlyphShape _glyphShape;
+    ScalingMode _scalingMode;
+    std::string _scalingFieldName;
+    double _scalingFieldRange[2];
+    double _dataScale;
+    double _scaleFactor;
+    bool _normalizeScale;
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkPolyDataAlgorithm> _glyphSource;
+    vtkSmartPointer<vtkGlyph3DMapper> _glyphMapper;
+    vtkSmartPointer<vtkMaskPoints> _maskPoints;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/GraphicsObject.cpp
===================================================================
--- vtkvis/tags/1.8.4/GraphicsObject.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/GraphicsObject.cpp	(revision 6184)
@@ -0,0 +1,52 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include "GraphicsObject.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+void GraphicsObject::setDataSet(DataSet *dataSet,
+                                Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        if (renderer != NULL &&
+            dataSet != NULL &&
+            renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+        } else if (_dataSet != NULL) {
+            _dataSet->getScalarRange(_dataRange);
+        }
+
+        update();
+    }
+}
+
+void GraphicsObject::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+    if (renderer == NULL) {
+        ERROR("NULL renderer");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+    }
+}
Index: vtkvis/tags/1.8.4/GraphicsObject.h
===================================================================
--- vtkvis/tags/1.8.4/GraphicsObject.h	(revision 6184)
+++ vtkvis/tags/1.8.4/GraphicsObject.h	(revision 6184)
@@ -0,0 +1,1002 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_GRAPHICS_OBJECT_H
+#define VTKVIS_GRAPHICS_OBJECT_H
+
+#include <cmath>
+#include <string>
+
+#include <vtkSmartPointer.h>
+#include <vtkProp.h>
+#include <vtkActor2D.h>
+#include <vtkProp3D.h>
+#include <vtkProp3DCollection.h>
+#include <vtkAssembly.h>
+#include <vtkActor.h>
+#include <vtkImageSlice.h>
+#include <vtkImageActor.h>
+#include <vtkVolume.h>
+#include <vtkProperty.h>
+#include <vtkImageProperty.h>
+#include <vtkVolumeProperty.h>
+#include <vtkMath.h>
+#include <vtkMatrix4x4.h>
+#include <vtkPlaneCollection.h>
+
+#include "DataSet.h"
+#include "Math.h"
+#include "Trace.h"
+
+namespace VtkVis {
+
+class Renderer;
+
+/**
+ * \brief Base class for graphics objects
+ */
+class GraphicsObject {
+public:
+    enum CullFace {
+        CULL_FRONT,
+        CULL_BACK,
+        CULL_FRONT_AND_BACK
+    };
+    enum ShadingModel {
+        SHADE_FLAT = VTK_FLAT,
+        SHADE_GOURAUD = VTK_GOURAUD,
+        SHADE_PHONG = VTK_PHONG
+    };
+
+    GraphicsObject() :
+        _dataSet(NULL),
+        _opacity(1.0),
+        _edgeWidth(1.0f),
+        _pointSize(1.0f),
+        _lighting(true),
+        _cullFace(CULL_BACK),
+        _faceCulling(false)
+    {
+        _dataRange[0] = 0;
+        _dataRange[1] = 1;
+        _color[0] = 1.0f;
+        _color[1] = 1.0f;
+        _color[2] = 1.0f;
+        _edgeColor[0] = 0.0f;
+        _edgeColor[1] = 0.0f;
+        _edgeColor[2] = 0.0f;
+    }
+
+    virtual ~GraphicsObject()
+    {}
+
+    /**
+     * \brief Return the name of the sublass of GraphicsObject
+     */
+    virtual const char *getClassName() const = 0;
+
+    /**
+     * \brief Specify input DataSet and information on cumulative data ranges
+     *
+     * Default implementation calls update() and stores scalarRange to
+     * _dataRange based on cumulative range settings
+     *
+     * \param[in] dataSet DataSet to use in rendering
+     * \param[in] renderer Pointer to Renderer -- may be used to get 
+     * cumulative data ranges
+     */
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    /**
+     * \brief Called when scalar or vector field changes (e.g. active field) or 
+     * cumulative ranges or settings change
+     *
+     * \param[in] renderer Pointer to Renderer -- may be used to get 
+     * cumulative data ranges
+     */
+    virtual void updateRanges(Renderer *renderer);
+
+    /**
+     * \brief Return the DataSet this object renders
+     */
+    inline DataSet *getDataSet()
+    {
+        return _dataSet;
+    }
+
+    /**
+     * \brief Return the VTK prop object to render
+     */
+    inline vtkProp *getProp()
+    {
+        return _prop;
+    }
+
+    /**
+     * \brief Cast the vktProp to a vtkActor2D
+     *
+     * \return NULL or a vtkActor2D pointer
+     */
+    inline vtkActor2D *getActor2D()
+    {
+        return vtkActor2D::SafeDownCast(_prop);
+    }
+
+    /**
+     * \brief Cast the vktProp to a vtkProp3D
+     *
+     * \return NULL or a vtkProp3D pointer
+     */
+    inline vtkProp3D *getProp3D()
+    {
+        return vtkProp3D::SafeDownCast(_prop);
+    }
+
+    /**
+     * \brief Cast the vktProp to a vtkActor
+     *
+     * \return NULL or a vtkActor pointer
+     */
+    inline vtkActor *getActor()
+    {
+        return vtkActor::SafeDownCast(_prop);
+    }
+
+    /**
+     * \brief Cast the vktProp to a vtkImageSlice
+     *
+     * \return NULL or a vtkImageSlice pointer
+     */
+    inline vtkImageSlice *getImageSlice()
+    {
+        return vtkImageSlice::SafeDownCast(_prop);
+    }
+
+    /**
+     * \brief Cast the vktProp to a vtkImageActor
+     *
+     * \return NULL or a vtkImageActor pointer
+     */
+    inline vtkImageActor *getImageActor()
+    {
+        return vtkImageActor::SafeDownCast(_prop);
+    }
+
+    /**
+     * \brief Cast the vktProp to a vtkVolume
+     *
+     * \return NULL or a vtkVolume pointer
+     */
+    inline vtkVolume *getVolume()
+    {
+        return vtkVolume::SafeDownCast(_prop);
+    }
+
+    /**
+     * \brief Cast the vktProp to a vtkAssembly
+     *
+     * \return NULL or a vtkAssembly pointer
+     */
+    inline vtkAssembly *getAssembly()
+    {
+        return vtkAssembly::SafeDownCast(_prop);
+    }
+
+    /**
+     * \brief If the object has a 2D overlay prop, return it
+     *
+     * \return NULL or a vtkProp pointer
+     */
+    virtual vtkProp *getOverlayProp()
+    {
+        return NULL;
+    }
+
+    /**
+     * \brief Set an additional transform on the prop
+     * 
+     * prop must be a vtkProp3D.  The transform is 
+     * concatenated with the internal transform created
+     * by setting the prop position, orientation and scale
+     */
+    virtual void setTransform(vtkMatrix4x4 *matrix)
+    {
+        if (getProp3D() != NULL) {
+            getProp3D()->SetUserMatrix(matrix);
+        }
+    }
+
+    /**
+     * \brief Set the prop center of rotation
+     *
+     * \param[in] origin The point about which the object rotates
+     */
+    virtual void setOrigin(double origin[3])
+    {
+        if (getProp3D() != NULL) {
+            getProp3D()->SetOrigin(origin);
+        }
+    }
+
+    /**
+     * \brief Set the prop orientation with a quaternion
+     *
+     * \param[in] quat Quaternion with scalar part first
+     */
+    virtual void setOrientation(double quat[4])
+    {
+        if (getProp3D() != NULL) {
+            double angleAxis[4];
+            quatToAngleAxis(quat, angleAxis);
+            setOrientation(angleAxis[0], &angleAxis[1]);
+        }
+    }
+
+    /**
+     * \brief Set the prop orientation with a rotation about an axis
+     *
+     * \param[in] angle Angle in degrees
+     * \param[in] axis Axis of rotation
+     */
+    virtual void setOrientation(double angle, double axis[3])
+    {
+        if (getProp3D() != NULL) {
+            getProp3D()->SetOrientation(0, 0, 0);
+            getProp3D()->RotateWXYZ(angle, axis[0], axis[1], axis[2]);
+        }
+    }
+
+    /**
+     * \brief Set the prop position
+     *
+     * \param[in] pos Position in world coordinates
+     */
+    virtual void setPosition(double pos[3])
+    {
+        if (getProp3D() != NULL) {
+            getProp3D()->SetPosition(pos);
+        }
+    }
+
+    /**
+     * \brief Set 2D aspect ratio scaling
+     *
+     * \param aspect 0=no scaling, otherwise aspect
+     * is horiz/vertical ratio
+     */
+    virtual void setAspect(double aspect)
+    {
+        double scale[3];
+        scale[0] = scale[1] = scale[2] = 1.;
+
+        if (aspect == 0.0) {
+            setScale(scale);
+            return;
+        }
+        if (_dataSet == NULL ||
+            _dataSet->getVtkDataSet() == NULL) {
+            TRACE("Not setting aspect for object with no data set");
+            return;
+        }
+
+        PrincipalPlane plane;
+        if (!_dataSet->is2D(&plane)) {
+            TRACE("Not setting aspect for 3D object %s", getClassName());
+            return;
+        }
+
+        double bounds[6];
+        vtkDataSet *ds = _dataSet->getVtkDataSet();
+        ds->GetBounds(bounds);
+        double size[3];
+        size[0] = bounds[1] - bounds[0];
+        size[1] = bounds[3] - bounds[2];
+        size[2] = bounds[5] - bounds[4];
+
+        if (aspect == 1.0) {
+            // Square
+            switch (plane) {
+            case PLANE_XY: {
+                if (size[0] > size[1] && size[1] > 0.0) {
+                    scale[1] = size[0] / size[1];
+                } else if (size[1] > size[0] && size[0] > 0.0) {
+                    scale[0] = size[1] / size[0];
+                }
+            }
+                break;
+            case PLANE_ZY: {
+                if (size[1] > size[2] && size[2] > 0.0) {
+                    scale[2] = size[1] / size[2];
+                } else if (size[2] > size[1] && size[1] > 0.0) {
+                    scale[1] = size[2] / size[1];
+                }
+            }
+                break;
+            case PLANE_XZ: {
+                if (size[0] > size[2] && size[2] > 0.0) {
+                    scale[2] = size[0] / size[2];
+                } else if (size[2] > size[0] && size[0] > 0.0) {
+                    scale[0] = size[2] / size[0];
+                }
+            }
+                break;
+            default:
+                break;
+            }
+        } else {
+            switch (plane) {
+            case PLANE_XY: {
+                if (aspect > 1.0) {
+                    if (size[0] > size[1] && size[1] > 0.0) {
+                        scale[1] = (size[0] / aspect) / size[1];
+                    } else if (size[0] > 0.0) {
+                        scale[0] = (size[1] * aspect) / size[0];
+                    }
+                } else {
+                    if (size[1] > size[0] && size[0] > 0.0) {
+                        scale[0] = (size[1] * aspect) / size[0];
+                    } else if (size[1] > 0.0) {
+                        scale[1] = (size[0] / aspect) / size[1];
+                    }
+                }
+            }
+                break;
+            case PLANE_ZY: {
+                if (aspect > 1.0) {
+                    if (size[2] > size[1] && size[1] > 0.0) {
+                        scale[1] = (size[2] / aspect) / size[1];
+                    } else if (size[2] > 0.0) {
+                        scale[2] = (size[1] * aspect) / size[2];
+                    }
+                } else {
+                    if (size[1] > size[2] && size[2] > 0.0) {
+                        scale[2] = (size[1] * aspect) / size[2];
+                    } else if (size[1] > 0.0) {
+                        scale[1] = (size[2] / aspect) / size[1];
+                    }
+                }
+            }
+                break;
+            case PLANE_XZ: {
+                if (aspect > 1.0) {
+                    if (size[0] > size[2] && size[2] > 0.0) {
+                        scale[2] = (size[0] / aspect) / size[2];
+                    } else if (size[0] > 0.0) {
+                        scale[0] = (size[2] * aspect) / size[0];
+                    }
+                } else {
+                    if (size[2] > size[0] && size[0] > 0.0) {
+                        scale[0] = (size[2] * aspect) / size[0];
+                    } else if (size[2] > 0.0) {
+                        scale[2] = (size[0] / aspect) / size[2];
+                    }
+                }
+            }
+            default:
+                break;
+            }
+        }
+
+        TRACE("obj %g,%g,%g", size[0], size[1], size[2]);
+        TRACE("Setting scale to %g,%g,%g", scale[0], scale[1], scale[2]);
+        setScale(scale);
+    }
+
+    /**
+     * \brief Get the prop scaling
+     *
+     * \param[out] scale Scaling in x,y,z
+     */
+    virtual void getScale(double scale[3])
+    {
+        if (getProp3D() != NULL) {
+            getProp3D()->GetScale(scale);
+        } else {
+            scale[0] = scale[1] = scale[2] = 1.0;
+        }
+    }
+
+    /**
+     * \brief Set the prop scaling
+     *
+     * \param[in] scale Scaling in x,y,z
+     */
+    virtual void setScale(double scale[3])
+    {
+        if (getProp3D() != NULL) {
+            getProp3D()->SetScale(scale);
+        }
+    }
+
+    /**
+     * \brief Get the physical bounds of the prop
+     *
+     * If the prop is scaled, these bounds will be
+     * scaled as well
+     */
+    virtual double *getBounds()
+    {
+        if (getProp() != NULL)
+            return getProp()->GetBounds();
+        else
+            return NULL;
+    }
+
+    /**
+     * \brief Get the data bounds of the prop
+     *
+     * If the prop is scaled, these bounds will NOT
+     * be scaled, they will reflect the unscaled data
+     * bounds.
+     */
+    virtual double *getUnscaledBounds()
+    {
+        if (getProp3D() != NULL) {
+            double scale[3];
+            getProp3D()->GetScale(scale);
+            if (scale[0] == scale[1] && scale[1] == scale[2] &&
+                scale[0] == 1.0) {
+                return getBounds();
+            } else if (getBounds() != NULL) {
+                _unscaledBounds[0] = getBounds()[0] / scale[0];
+                _unscaledBounds[1] = getBounds()[1] / scale[0];
+                _unscaledBounds[2] = getBounds()[2] / scale[1];
+                _unscaledBounds[3] = getBounds()[3] / scale[1];
+                _unscaledBounds[4] = getBounds()[4] / scale[2];
+                _unscaledBounds[5] = getBounds()[5] / scale[2];
+                return _unscaledBounds;
+            }
+        }
+        return getBounds();
+    }
+
+    /**
+     * \brief Toggle visibility of the prop
+     */
+    virtual void setVisibility(bool state)
+    {
+        if (_prop != NULL)
+            _prop->SetVisibility((state ? 1 : 0));
+    }
+
+    /**
+     * \brief Get visibility state of the prop
+     */
+    virtual bool getVisibility()
+    {
+        if (_prop != NULL &&
+            _prop->GetVisibility() != 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * \brief Set the opacity of the object
+     *
+     * The prop must be a vtkActor or vtkAssembly
+     */
+    virtual void setOpacity(double opacity)
+    {
+        _opacity = opacity;
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetOpacity(opacity);
+            if (_faceCulling && _opacity < 1.0) {
+                setCulling(getActor()->GetProperty(), false);
+                TRACE("Culling off");
+            } else if (_faceCulling && _opacity == 1.0) {
+                setCulling(getActor()->GetProperty(), true);
+                TRACE("Culling on");
+            }
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetOpacity(opacity);
+                    if (_faceCulling && _opacity < 1.0) {
+                        setCulling(vtkActor::SafeDownCast(prop)->GetProperty(), false);
+                        TRACE("Culling off");
+                    } else if (_faceCulling && _opacity == 1.0) {
+                        setCulling(vtkActor::SafeDownCast(prop)->GetProperty(), true);
+                        TRACE("Culling on");
+                    }
+                } else if (vtkImageActor::SafeDownCast(prop) != NULL) {
+                    vtkImageActor::SafeDownCast(prop)->GetProperty()->SetOpacity(opacity);
+                }
+            }
+        } else if (getImageActor() != NULL) {
+            getImageActor()->GetProperty()->SetOpacity(_opacity);
+        }
+    }
+
+    /**
+     * \brief Get the opacity of the object
+     */
+    inline double getOpacity()
+    {
+        return _opacity;
+    }
+
+    /**
+     * \brief Set the ambient material coefficient
+     */
+    virtual void setAmbient(double ambient)
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetAmbient(ambient);
+        } else if (getImageActor() != NULL) {
+            getImageActor()->GetProperty()->SetAmbient(ambient);
+        } else if (getVolume() != NULL) {
+            getVolume()->GetProperty()->SetAmbient(ambient);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetAmbient(ambient);
+                } else if (vtkImageActor::SafeDownCast(prop) != NULL) {
+                    vtkImageActor::SafeDownCast(prop)->GetProperty()->SetAmbient(ambient);
+                } else if (vtkVolume::SafeDownCast(prop) != NULL) {
+                    vtkVolume::SafeDownCast(prop)->GetProperty()->SetAmbient(ambient);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set the diffuse material coefficient
+     */
+    virtual void setDiffuse(double diffuse)
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetDiffuse(diffuse);
+        } else if (getImageActor() != NULL) {
+            getImageActor()->GetProperty()->SetDiffuse(diffuse);
+        } else if (getVolume() != NULL) {
+            getVolume()->GetProperty()->SetDiffuse(diffuse);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetDiffuse(diffuse);
+                } else if (vtkImageActor::SafeDownCast(prop) != NULL) {
+                    vtkImageActor::SafeDownCast(prop)->GetProperty()->SetDiffuse(diffuse);
+                } else if (vtkVolume::SafeDownCast(prop) != NULL) {
+                    vtkVolume::SafeDownCast(prop)->GetProperty()->SetDiffuse(diffuse);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set the specular material coefficient and power
+     */
+    virtual void setSpecular(double coeff, double power)
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetSpecular(coeff);
+            getActor()->GetProperty()->SetSpecularPower(power);
+        } else if (getVolume() != NULL) {
+            getVolume()->GetProperty()->SetSpecular(coeff);
+            getVolume()->GetProperty()->SetSpecularPower(power);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetSpecular(coeff);
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetSpecularPower(power);
+                } else if (vtkVolume::SafeDownCast(prop) != NULL) {
+                    vtkVolume::SafeDownCast(prop)->GetProperty()->SetSpecular(coeff);
+                    vtkVolume::SafeDownCast(prop)->GetProperty()->SetSpecularPower(power);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set material properties (coefficients and specular power) of object
+     */
+    virtual void setMaterial(double ambient, double diffuse, double specular, double specPower)
+    {
+        if (getActor() != NULL) {
+            vtkProperty *property = getActor()->GetProperty();
+            property->SetAmbient(ambient);
+            property->SetDiffuse(diffuse);
+            property->SetSpecular(specular);
+            property->SetSpecularPower(specPower);
+        } else if (getImageActor() != NULL) {
+            vtkImageProperty *property = getImageActor()->GetProperty();
+            property->SetAmbient(ambient);
+            property->SetDiffuse(diffuse);
+        } else if (getVolume() != NULL) {
+            vtkVolumeProperty *property = getVolume()->GetProperty();
+            property->SetAmbient(ambient);
+            property->SetDiffuse(diffuse);
+            property->SetSpecular(specular);
+            property->SetSpecularPower(specPower);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkProperty *property = vtkActor::SafeDownCast(prop)->GetProperty();
+                    property->SetAmbient(ambient);
+                    property->SetDiffuse(diffuse);
+                    property->SetSpecular(specular);
+                    property->SetSpecularPower(specPower);
+                } else if (vtkImageActor::SafeDownCast(prop) != NULL) {
+                    vtkImageProperty *property = vtkImageActor::SafeDownCast(prop)->GetProperty();
+                    property->SetAmbient(ambient);
+                    property->SetDiffuse(diffuse);
+                } else if (vtkVolume::SafeDownCast(prop) != NULL) {
+                    vtkVolumeProperty *property = vtkVolume::SafeDownCast(prop)->GetProperty();
+                    property->SetAmbient(ambient);
+                    property->SetDiffuse(diffuse);
+                    property->SetSpecular(specular);
+                    property->SetSpecularPower(specPower);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set the material color (sets ambient, diffuse, and specular)
+     */
+    virtual void setColor(float color[3])
+    {
+        for (int i = 0; i < 3; i++)
+            _color[i] = color[i];
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetColor(color[0], color[1], color[2]);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetColor(color[0], color[1], color[2]);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set the material ambient color
+     */
+    virtual void setAmbientColor(float color[3])
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetAmbientColor(color[0], color[1], color[2]);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetAmbientColor(color[0], color[1], color[2]);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set the material diffuse color
+     */
+    virtual void setDiffuseColor(float color[3])
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetDiffuseColor(color[0], color[1], color[2]);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetDiffuseColor(color[0], color[1], color[2]);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set the material specular color
+     */
+    virtual void setSpecularColor(float color[3])
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetSpecularColor(color[0], color[1], color[2]);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetSpecularColor(color[0], color[1], color[2]);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Toggle lighting of the prop
+     */
+    virtual void setLighting(bool state)
+    {
+        _lighting = state;
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetLighting((state ? 1 : 0));
+        } else if (getVolume() != NULL) {
+            getVolume()->GetProperty()->SetShade((state ? 1 : 0));
+         } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetLighting((state ? 1 : 0));
+                } else if (vtkVolume::SafeDownCast(prop) != NULL) {
+                    vtkVolume::SafeDownCast(prop)->GetProperty()->SetShade((state ? 1 : 0));
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set shading interpolation model of the prop
+     */
+    virtual void setShadingModel(ShadingModel state)
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetInterpolation(state);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetInterpolation(state);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Toggle drawing of edges
+     */
+    virtual void setEdgeVisibility(bool state)
+    {
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set color of edges
+     */
+    virtual void setEdgeColor(float color[3])
+    {
+        for (int i = 0; i < 3; i++)
+            _edgeColor[i] = color[i];
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetEdgeColor(color[0], color[1], color[2]);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetEdgeColor(color[0], color[1], color[2]);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set pixel width of edges
+     *
+     * NOTE: May be a no-op if OpenGL implementation doesn't support wide lines
+     */
+    virtual void setEdgeWidth(float width)
+    {
+        _edgeWidth = width;
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetLineWidth(width);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetLineWidth(width);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Set point size
+     *
+     * NOTE: May be a no-op if OpenGL implementation doesn't support wide points
+     */
+    virtual void setPointSize(float size)
+    {
+        _pointSize = size;
+        if (getActor() != NULL) {
+            getActor()->GetProperty()->SetPointSize(size);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    vtkActor::SafeDownCast(prop)->GetProperty()->SetPointSize(size);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Toggle wireframe rendering of prop
+     */
+    virtual void setWireframe(bool state)
+    {
+        if (getActor() != NULL) {
+            if (state) {
+                getActor()->GetProperty()->SetRepresentationToWireframe();
+                getActor()->GetProperty()->LightingOff();
+            } else {
+                getActor()->GetProperty()->SetRepresentationToSurface();
+                setLighting(_lighting);
+            }
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    if (state) {
+                        vtkActor::SafeDownCast(prop)->GetProperty()->SetRepresentationToWireframe();
+                        vtkActor::SafeDownCast(prop)->GetProperty()->LightingOff();
+                    } else {
+                        vtkActor::SafeDownCast(prop)->GetProperty()->SetRepresentationToSurface();
+                        vtkActor::SafeDownCast(prop)->GetProperty()->SetLighting((_lighting ? 1 : 0));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Toggle culling of selected CullFace
+     */
+    virtual void setCulling(bool state)
+    {
+        _faceCulling = state;
+        if (state && _opacity < 1.0)
+            return;
+        if (getActor() != NULL) {
+            setCulling(getActor()->GetProperty(), state);
+        } else if (getAssembly() != NULL) {
+            vtkProp3DCollection *props = getAssembly()->GetParts();
+            vtkProp3D *prop;
+            props->InitTraversal();
+            while ((prop = props->GetNextProp3D()) != NULL) {
+                if (vtkActor::SafeDownCast(prop) != NULL) {
+                    setCulling(vtkActor::SafeDownCast(prop)->GetProperty(), state);
+                }
+            }
+        }
+    }
+
+    /**
+     * \brief Specify which face(s) to cull when culling is enabled
+     */
+    virtual void setCullFace(CullFace cull)
+    {
+        _cullFace = cull;
+        setCulling(_faceCulling);
+    }
+
+    /**
+     * \brief Subclasses need to implement setting clipping planes in their mappers
+     */
+    virtual void setClippingPlanes(vtkPlaneCollection *planes) = 0;
+
+protected:
+    /**
+     * \brief Create and initialize a VTK Prop to render the object
+     */
+    virtual void initProp()
+    {
+        if (_prop == NULL) {
+            _prop = vtkSmartPointer<vtkActor>::New();
+            vtkProperty *property = getActor()->GetProperty();
+            property->SetColor(_color[0], _color[1], _color[2]);
+            property->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+            property->SetLineWidth(_edgeWidth);
+            property->SetPointSize(_pointSize);
+            property->EdgeVisibilityOff();
+            if (_dataSet != NULL)
+                _opacity = _dataSet->getOpacity();
+            property->SetOpacity(_opacity);
+            property->SetAmbient(.2);
+            if (!_lighting)
+                property->LightingOff();
+            if (_faceCulling && _opacity == 1.0) {
+                setCulling(property, true);
+            }
+            if (_dataSet != NULL)
+                setVisibility(_dataSet->getVisibility());
+        }
+    }
+
+    /**
+     * \brief Subclasses implement this to create the VTK pipeline
+     * on a state change (e.g. new DataSet)
+     */
+    virtual void update() = 0;
+
+    /**
+     * \brief Convenience method to set culling state on a vtkProperty
+     *
+     * Note: Does not change the culling state flag of this GraphicsObject
+     */
+    virtual void setCulling(vtkProperty *property, bool state)
+    {
+        switch (_cullFace) {
+        case CULL_FRONT:
+            property->SetFrontfaceCulling((state ? 1 : 0));
+            property->BackfaceCullingOff();
+            break;
+        case CULL_BACK:
+            property->SetBackfaceCulling((state ? 1 : 0));
+            property->FrontfaceCullingOff();
+            break;
+        case CULL_FRONT_AND_BACK:
+            property->SetBackfaceCulling((state ? 1 : 0));
+            property->SetFrontfaceCulling((state ? 1 : 0));
+            break;
+        }
+    }
+
+    DataSet *_dataSet;
+    double _dataRange[2];
+    double _unscaledBounds[6];
+    double _opacity;
+    float _color[3];
+    float _edgeColor[3];
+    float _edgeWidth;
+    float _pointSize;
+    bool _lighting;
+    CullFace _cullFace;
+    bool _faceCulling;
+
+    vtkSmartPointer<vtkProp> _prop;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Group.cpp
===================================================================
--- vtkvis/tags/1.8.4/Group.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Group.cpp	(revision 6184)
@@ -0,0 +1,82 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include "Group.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Group::Group()
+{
+}
+
+Group::~Group()
+{
+}
+
+/**
+ * \brief Create and initialize VTK Props to render the Group
+ */
+void Group::initProp()
+{
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkAssembly>::New();
+    }
+}
+
+void Group::update()
+{
+    initProp();
+}
+
+void Group::addChild(const NodeId& name, GraphicsObject *obj)
+{
+    if (obj == NULL)
+        return;
+    if (obj->getProp3D() != NULL) {
+        getAssembly()->AddPart(obj->getProp3D());
+    }
+    _nodes[name] = obj;
+}
+
+GraphicsObject *Group::getChild(const NodeId& name)
+{
+    NodeHashmap::iterator itr = _nodes.find(name);
+    if (itr == _nodes.end()) {
+        return NULL;
+    } else {
+        return itr->second;
+    }
+}
+
+GraphicsObject *Group::removeChild(const NodeId& name)
+{
+    NodeHashmap::iterator itr = _nodes.find(name);
+    if (itr == _nodes.end()) {
+        ERROR("Node not found: '%s'", name.c_str());
+        return NULL;
+    }
+    GraphicsObject *obj = itr->second;
+    if (obj->getProp3D() != NULL) {
+        getAssembly()->RemovePart(obj->getProp3D());
+    }
+    _nodes.erase(itr);
+    return obj;
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Group::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    for (NodeHashmap::iterator itr = _nodes.begin();
+         itr != _nodes.end(); ++itr) {
+        itr->second->setClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Group.h
===================================================================
--- vtkvis/tags/1.8.4/Group.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Group.h	(revision 6184)
@@ -0,0 +1,68 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_GROUP_H
+#define VTKVIS_GROUP_H
+
+#include <tr1/unordered_map>
+
+#include <vtkProp3D.h>
+
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Collection of shapes with grouping
+ */
+class Group : public GraphicsObject {
+public:
+    typedef std::string NodeId;
+    typedef std::tr1::unordered_map<NodeId, GraphicsObject *> NodeHashmap;
+
+    Group();
+    virtual ~Group();
+
+    virtual const char *getClassName() const 
+    {
+        return "Group";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer)
+    {
+        assert(dataSet == NULL);
+        update();
+    }
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void addChild(const NodeId& name, GraphicsObject *obj);
+
+    GraphicsObject *getChild(const NodeId& name);
+
+    void getChildren(std::vector<GraphicsObject *>& children)
+    {
+        for (NodeHashmap::iterator itr = _nodes.begin();
+             itr != _nodes.end(); ++itr) {
+            children.push_back(itr->second);
+        }
+    }
+
+    GraphicsObject *removeChild(const NodeId& name);
+
+private:
+    virtual void initProp();
+
+    virtual void update();
+
+    NodeHashmap _nodes;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/HeightMap.cpp
===================================================================
--- vtkvis/tags/1.8.4/HeightMap.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/HeightMap.cpp	(revision 6184)
@@ -0,0 +1,1285 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCellDataToPointData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkProperty.h>
+#include <vtkImageData.h>
+#include <vtkLookupTable.h>
+#include <vtkTransform.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkGaussianSplatter.h>
+#include <vtkExtractVOI.h>
+#include <vtkDataSetSurfaceFilter.h>
+#include <vtkContourFilter.h>
+#include <vtkStripper.h>
+#include <vtkWarpScalar.h>
+#include <vtkPropAssembly.h>
+#include <vtkCutter.h>
+#include <vtkPlane.h>
+
+#include "HeightMap.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+#define TRANSLATE_TO_ZORIGIN
+
+HeightMap::HeightMap(int numContours, double heightScale) :
+    GraphicsObject(),
+    _numContours(numContours),
+    _contourColorMap(false),
+    _contourEdgeWidth(1.0),
+    _warpScale(heightScale),
+    _sliceAxis(Z_AXIS),
+    _pipelineInitialized(false),
+    _cloudStyle(CLOUD_MESH),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _contourEdgeColor[0] = 0.0f;
+    _contourEdgeColor[1] = 0.0f;
+    _contourEdgeColor[2] = 0.0f;
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+HeightMap::HeightMap(const std::vector<double>& contours, double heightScale) :
+    GraphicsObject(),
+    _numContours(contours.size()),
+    _contours(contours),
+    _contourColorMap(false),
+    _contourEdgeWidth(1.0),
+    _warpScale(heightScale),
+    _sliceAxis(Z_AXIS),
+    _pipelineInitialized(false),
+    _cloudStyle(CLOUD_MESH),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _contourEdgeColor[0] = 0.0f;
+    _contourEdgeColor[1] = 0.0f;
+    _contourEdgeColor[2] = 0.0f;
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+HeightMap::~HeightMap()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting HeightMap for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting HeightMap with NULL DataSet");
+#endif
+}
+
+void HeightMap::setDataSet(DataSet *dataSet,
+                           Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+        _renderer = renderer;
+
+        if (_dataSet != NULL) {
+            TRACE("DataSet name: '%s' type: %s",
+                  _dataSet->getName().c_str(),
+                  _dataSet->getVtkType());
+
+            if (renderer->getUseCumulativeRange()) {
+                renderer->getCumulativeDataRange(_dataRange,
+                                                 _dataSet->getActiveScalarsName(),
+                                                 1);
+                const char *activeVectors = _dataSet->getActiveVectorsName();
+                if (activeVectors != NULL) {
+                    renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                                     activeVectors,
+                                                     3);
+                    for (int i = 0; i < 3; i++) {
+                        renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                         activeVectors,
+                                                         3, i);
+                    }
+                }
+            } else {
+                _dataSet->getScalarRange(_dataRange);
+                _dataSet->getVectorRange(_vectorMagnitudeRange);
+                for (int i = 0; i < 3; i++) {
+                    _dataSet->getVectorRange(_vectorComponentRange[i], i);
+                }
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * Compute a data scaling factor to make maximum height (at default _warpScale) 
+ * equivalent to largest dimension of data set
+ */
+void HeightMap::computeDataScale()
+{
+    if (_dataSet == NULL)
+        return;
+
+    double bounds[6];
+    double boundsRange = 0.0;
+    _dataSet->getBounds(bounds);
+    for (int i = 0; i < 6; i+=2) {
+        double r = bounds[i+1] - bounds[i];
+        if (r > boundsRange)
+            boundsRange = r;
+    }
+    double datRange = _dataRange[1] - _dataRange[0];
+    if (datRange < 1.0e-17) {
+        _dataScale = 1.0;
+    } else {
+        _dataScale = boundsRange / datRange;
+    }
+    TRACE("Bounds range: %g data range: %g scaling: %g",
+          boundsRange, datRange, _dataScale);
+}
+
+/**
+ * \brief Create and initialize VTK Props to render the colormapped dataset
+ */
+void HeightMap::initProp()
+{
+    if (_dsActor == NULL) {
+        _dsActor = vtkSmartPointer<vtkActor>::New();
+        _dsActor->GetProperty()->SetOpacity(_opacity);
+        _dsActor->GetProperty()->SetColor(_color[0],
+                                          _color[1],
+                                          _color[2]);
+        _dsActor->GetProperty()->SetEdgeColor(_edgeColor[0],
+                                              _edgeColor[1],
+                                              _edgeColor[2]);
+        _dsActor->GetProperty()->SetLineWidth(_edgeWidth);
+        _dsActor->GetProperty()->EdgeVisibilityOff();
+        _dsActor->GetProperty()->SetAmbient(0.2);
+        _dsActor->GetProperty()->LightingOn();
+    }
+    if (_contourActor == NULL) {
+        _contourActor = vtkSmartPointer<vtkActor>::New();
+        _contourActor->GetProperty()->SetOpacity(_opacity);
+        _contourActor->GetProperty()->SetColor(_contourEdgeColor[0],
+                                               _contourEdgeColor[1],
+                                               _contourEdgeColor[2]);
+        _contourActor->GetProperty()->SetEdgeColor(_contourEdgeColor[0],
+                                                   _contourEdgeColor[1],
+                                                   _contourEdgeColor[2]);
+        _contourActor->GetProperty()->SetLineWidth(_contourEdgeWidth);
+        _contourActor->GetProperty()->EdgeVisibilityOff();
+        _contourActor->GetProperty()->SetAmbient(1.0);
+        _contourActor->GetProperty()->LightingOff();
+    }
+    // Contour actor is added in update() if contours are produced
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkAssembly>::New();
+        getAssembly()->AddPart(_dsActor);
+    } else {
+        getAssembly()->RemovePart(_contourActor);
+    }
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void HeightMap::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    TRACE("DataSet: %s", _dataSet->getName().c_str());
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    computeDataScale();
+
+    // Contour filter to generate isolines
+    if (_contourFilter == NULL) {
+        _contourFilter = vtkSmartPointer<vtkContourFilter>::New();
+    }
+
+    // Mapper, actor to render color-mapped data set
+    if (_mapper == NULL) {
+        _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        // Map scalars through lookup table regardless of type
+        _mapper->SetColorModeToMapScalars();
+    }
+
+    if (!_pipelineInitialized) {
+        vtkAlgorithmOutput *dsOutput = NULL;
+        vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
+
+        if (ds->GetPointData() == NULL ||
+            ds->GetPointData()->GetScalars() == NULL) {
+            TRACE("No scalar point data in dataset %s", _dataSet->getName().c_str());
+            if (ds->GetCellData() != NULL &&
+                ds->GetCellData()->GetScalars() != NULL) {
+                cellToPtData = 
+                    vtkSmartPointer<vtkCellDataToPointData>::New();
+                cellToPtData->SetInputData(ds);
+                cellToPtData->PassCellDataOn();
+                dsOutput = cellToPtData->GetOutputPort();
+            } else {
+                USER_ERROR("No scalar field was found in the data set.");
+                return;
+            }
+        } else {
+            dsOutput = _dataSet->getProducerPort();
+        }
+
+        _splatter = NULL;
+
+        if (_dataSet->isCloud()) {
+            // DataSet is a point cloud
+            PrincipalPlane plane;
+            double offset;
+            if (_dataSet->is2D(&plane, &offset)) {
+                if (_cloudStyle == CLOUD_MESH) {
+                    // Result of Delaunay2D is a PolyData
+                    vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                    if (plane == PLANE_ZY) {
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->RotateWXYZ(90, 0, 1, 0);
+                        if (offset != 0.0) {
+                            trans->Translate(-offset, 0, 0);
+                        }
+                        mesher->SetTransform(trans);
+                        _sliceAxis = X_AXIS;
+                    } else if (plane == PLANE_XZ) {
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->RotateWXYZ(-90, 1, 0, 0);
+                        if (offset != 0.0) {
+                            trans->Translate(0, -offset, 0);
+                        }
+                        mesher->SetTransform(trans);
+                        _sliceAxis = Y_AXIS;
+                    } else if (offset != 0.0) {
+                        // XY with Z offset
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->Translate(0, 0, -offset);
+                        mesher->SetTransform(trans);
+                    }
+                    mesher->SetInputConnection(dsOutput);
+                    vtkAlgorithmOutput *warpOutput = initWarp(mesher->GetOutputPort());
+                    _mapper->SetInputConnection(warpOutput);
+                    _contourFilter->SetInputConnection(warpOutput);
+                } else {
+                    // _cloudStyle == CLOUD_SPLAT
+                    if (_splatter == NULL)
+                        _splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                    if (_volumeSlicer == NULL)
+                        _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
+                    _splatter->SetInputConnection(dsOutput);
+                    int dims[3];
+                    _splatter->GetSampleDimensions(dims);
+                    TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
+                    if (plane == PLANE_ZY) {
+                        dims[0] = 3;
+                        _volumeSlicer->SetVOI(1, 1, 0, dims[1]-1, 0, dims[1]-1);
+                        _sliceAxis = X_AXIS;
+                    } else if (plane == PLANE_XZ) {
+                        dims[1] = 3;
+                        _volumeSlicer->SetVOI(0, dims[0]-1, 1, 1, 0, dims[2]-1);
+                        _sliceAxis = Y_AXIS;
+                    } else {
+                        dims[2] = 3;
+                        _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, 1, 1);
+                    }
+                    _splatter->SetSampleDimensions(dims);
+                    double bounds[6];
+                    _splatter->Update();
+                    _splatter->GetModelBounds(bounds);
+                    TRACE("Model bounds: %g %g %g %g %g %g",
+                          bounds[0], bounds[1],
+                          bounds[2], bounds[3],
+                          bounds[4], bounds[5]);
+                    _volumeSlicer->SetInputConnection(_splatter->GetOutputPort());
+                    _volumeSlicer->SetSampleRate(1, 1, 1);
+                    vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                    gf->UseStripsOn();
+                    gf->SetInputConnection(_volumeSlicer->GetOutputPort());
+                    vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
+                    _mapper->SetInputConnection(warpOutput);
+                    _contourFilter->SetInputConnection(warpOutput);
+                }
+            } else {
+                // 3D point cloud
+                vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                gf->UseStripsOn();
+                if (_cloudStyle == CLOUD_MESH) {
+                     // Result of Delaunay3D mesher is unstructured grid
+                    vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+                    mesher->SetInputConnection(dsOutput);
+                    // Run the mesher
+                    mesher->Update();
+                    // Get bounds of resulting grid
+                    double bounds[6];
+                    mesher->GetOutput()->GetBounds(bounds);
+                    // Sample a plane within the grid bounding box
+                    vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
+                    cutter->SetInputConnection(mesher->GetOutputPort());
+                    if (_cutPlane == NULL) {
+                        _cutPlane = vtkSmartPointer<vtkPlane>::New();
+                    }
+                    _cutPlane->SetNormal(0, 0, 1);
+                    _cutPlane->SetOrigin(0,
+                                         0,
+                                         bounds[4] + (bounds[5]-bounds[4])/2.);
+                    cutter->SetCutFunction(_cutPlane);
+                    gf->SetInputConnection(cutter->GetOutputPort());
+                } else {
+                    // _cloudStyle == CLOUD_SPLAT
+                    if (_splatter == NULL)
+                        _splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                    _splatter->SetInputConnection(dsOutput);
+                    int dims[3];
+                    _splatter->GetSampleDimensions(dims);
+                    TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
+                    dims[2] = 3;
+                    _splatter->SetSampleDimensions(dims);
+                    double bounds[6];
+                    _splatter->Update();
+                    _splatter->GetModelBounds(bounds);
+                    TRACE("Model bounds: %g %g %g %g %g %g",
+                          bounds[0], bounds[1],
+                          bounds[2], bounds[3],
+                          bounds[4], bounds[5]);
+                    if (_volumeSlicer == NULL)
+                        _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
+                    _volumeSlicer->SetInputConnection(_splatter->GetOutputPort());
+                    _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, 1, 1);
+                    _volumeSlicer->SetSampleRate(1, 1, 1);
+                    gf->SetInputConnection(_volumeSlicer->GetOutputPort());
+                }
+                vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
+                _mapper->SetInputConnection(warpOutput);
+                _contourFilter->SetInputConnection(warpOutput);
+            }
+        } else if (vtkPolyData::SafeDownCast(ds) != NULL) {
+            // DataSet is a vtkPolyData with lines and/or polygons
+            vtkAlgorithmOutput *warpOutput = initWarp(dsOutput);
+            _mapper->SetInputConnection(warpOutput);
+            _contourFilter->SetInputConnection(warpOutput);
+        } else {
+            // DataSet is NOT a vtkPolyData
+            // Can be: image/volume/uniform grid, structured grid, unstructured grid, rectilinear grid
+            vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+            gf->UseStripsOn();
+            vtkImageData *imageData = vtkImageData::SafeDownCast(ds);
+            if (!_dataSet->is2D() && imageData != NULL) {
+                // 3D image/volume/uniform grid
+                if (_volumeSlicer == NULL)
+                    _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
+                int dims[3];
+                imageData->GetDimensions(dims);
+                TRACE("Image data dimensions: %d %d %d", dims[0], dims[1], dims[2]);
+                _volumeSlicer->SetInputConnection(dsOutput);
+                _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, (dims[2]-1)/2, (dims[2]-1)/2);
+                _volumeSlicer->SetSampleRate(1, 1, 1);
+                gf->SetInputConnection(_volumeSlicer->GetOutputPort());
+            } else if (!_dataSet->is2D() && imageData == NULL) {
+                // 3D structured grid, unstructured grid, or rectilinear grid
+                double bounds[6];
+                ds->GetBounds(bounds);
+                // Sample a plane within the grid bounding box
+                vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
+                cutter->SetInputConnection(dsOutput);
+                if (_cutPlane == NULL) {
+                    _cutPlane = vtkSmartPointer<vtkPlane>::New();
+                }
+                _cutPlane->SetNormal(0, 0, 1);
+                _cutPlane->SetOrigin(0,
+                                     0,
+                                     bounds[4] + (bounds[5]-bounds[4])/2.);
+                cutter->SetCutFunction(_cutPlane);
+                gf->SetInputConnection(cutter->GetOutputPort());
+            } else {
+                // 2D data
+                gf->SetInputConnection(dsOutput);
+            }
+            vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
+            _mapper->SetInputConnection(warpOutput);
+            _contourFilter->SetInputConnection(warpOutput);
+        }
+    }
+
+    _contourFilter->ComputeNormalsOff();
+    _contourFilter->ComputeGradientsOff();
+
+    // Speed up multiple contour computation at cost of extra memory use
+    if (_numContours > 1) {
+        _contourFilter->UseScalarTreeOn();
+    } else {
+        _contourFilter->UseScalarTreeOff();
+    }
+
+    _contourFilter->SetNumberOfContours(_numContours);
+
+    if (_contours.empty()) {
+        // Evenly spaced isovalues
+        if (_colorFieldRange[0] <= _colorFieldRange[1]) {
+            _contourFilter->GenerateValues(_numContours, _colorFieldRange[0], _colorFieldRange[1]);
+        } else {
+            _contourFilter->GenerateValues(_numContours, _dataRange[0], _dataRange[1]);
+        }
+    } else {
+        // User-supplied isovalues
+        for (int i = 0; i < _numContours; i++) {
+            _contourFilter->SetValue(i, _contours[i]);
+        }
+    }
+
+    initProp();
+
+    if (_contourMapper == NULL) {
+        _contourMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _contourMapper->ScalarVisibilityOff();
+        _contourMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        vtkSmartPointer<vtkStripper> stripper = vtkSmartPointer<vtkStripper>::New();
+        stripper->SetInputConnection(_contourFilter->GetOutputPort());
+        _contourMapper->SetInputConnection(stripper->GetOutputPort());
+        _contourActor->SetMapper(_contourMapper);
+        //_contourActor->InterpolateScalarsBeforeMappingOn();
+    }
+
+    setInterpolateBeforeMapping(true);
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+        setColorMode(_colorMode);
+    } else if (!_pipelineInitialized) {
+        double *rangePtr = _colorFieldRange;
+        if (_colorFieldRange[0] > _colorFieldRange[1]) {
+            rangePtr = NULL;
+        }
+        setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+    }
+
+    _pipelineInitialized = true;
+
+    TRACE("DataSet active scalars: %s", _dataSet->getActiveScalarsName());
+    // Ensure updated dataScale is applied
+    if (_warp != NULL) {
+        _warp->SetScaleFactor(_warpScale * _dataScale);
+    }
+#ifdef TRANSLATE_TO_ZORIGIN
+    double pos[3];
+    pos[0] = 0;
+    pos[1] = 0;
+    pos[2] = - _warpScale * _dataScale * _dataRange[0];
+    setPosition(pos);
+    TRACE("Z_POS: %g", pos[2]);
+#endif
+    _dsActor->SetMapper(_mapper);
+
+    _mapper->Update();
+    _contourMapper->Update();
+
+    // Only add contour actor to assembly if contours were
+    // produced, in order to prevent messing up assembly bounds
+    double bounds[6];
+    _contourActor->GetBounds(bounds);
+    if (bounds[0] <= bounds[1]) {
+        getAssembly()->AddPart(_contourActor);
+    }
+}
+
+void HeightMap::setCloudStyle(CloudStyle style)
+{
+    if (style != _cloudStyle) {
+        _cloudStyle = style;
+        if (_dataSet != NULL) {
+            _pipelineInitialized = false;
+            update();
+        }
+    }
+}
+
+void HeightMap::setInterpolateBeforeMapping(bool state)
+{
+    if (_mapper != NULL) {
+        _mapper->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
+    }
+}
+
+void HeightMap::setAspect(double aspect)
+{
+    double bounds[6];
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+    ds->GetBounds(bounds);
+    double size[3];
+    size[0] = bounds[1] - bounds[0];
+    size[1] = bounds[3] - bounds[2];
+    size[2] = bounds[5] - bounds[4];
+    double scale[3];
+    scale[0] = scale[1] = scale[2] = 1.;
+
+    if (aspect == 1.0) {
+        // Square
+        switch (_sliceAxis) {
+        case X_AXIS: {
+            if (size[1] > size[2] && size[2] > 0.0) {
+                scale[2] = size[1] / size[2];
+            } else if (size[2] > size[1] && size[1] > 0.0) {
+                scale[1] = size[2] / size[1];
+            }
+        }
+            break;
+        case Y_AXIS: {
+            if (size[0] > size[2] && size[2] > 0.0) {
+                scale[2] = size[0] / size[2];
+            } else if (size[2] > size[0] && size[0] > 0.0) {
+                scale[0] = size[2] / size[0];
+            }
+        }
+            break;
+        case Z_AXIS: {
+            if (size[0] > size[1] && size[1] > 0.0) {
+                scale[1] = size[0] / size[1];
+            } else if (size[1] > size[0] && size[0] > 0.0) {
+                scale[0] = size[1] / size[0];
+            }
+        }
+            break;
+        }
+    } else if (aspect != 0.0) {
+        switch (_sliceAxis) {
+        case X_AXIS: {
+            if (aspect > 1.0) {
+                if (size[2] > size[1] && size[1] > 0.0) {
+                    scale[1] = (size[2] / aspect) / size[1];
+                } else if (size[2] > 0.0) {
+                    scale[2] = (size[1] * aspect) / size[2];
+                }
+            } else {
+                if (size[1] > size[2] && size[2] > 0.0) {
+                    scale[2] = (size[1] * aspect) / size[2];
+                } else if (size[1] > 0.0) {
+                    scale[1] = (size[2] / aspect) / size[1];
+                }
+            }
+        }
+            break;
+        case Y_AXIS: {
+            if (aspect > 1.0) {
+                if (size[0] > size[2] && size[2] > 0.0) {
+                    scale[2] = (size[0] / aspect) / size[2];
+                } else if (size[0] > 0.0) {
+                    scale[0] = (size[2] * aspect) / size[0];
+                }
+            } else {
+                if (size[2] > size[0] && size[0] > 0.0) {
+                    scale[0] = (size[2] * aspect) / size[0];
+                } else if (size[2] > 0.0) {
+                    scale[2] = (size[0] / aspect) / size[2];
+                }
+            }
+        }
+            break;
+        case Z_AXIS: {
+            if (aspect > 1.0) {
+                if (size[0] > size[1] && size[1] > 0.0) {
+                    scale[1] = (size[0] / aspect) / size[1];
+                } else if (size[0] > 0.0) {
+                    scale[0] = (size[1] * aspect) / size[0];
+                }
+            } else {
+                if (size[1] > size[0] && size[0] > 0.0) {
+                    scale[0] = (size[1] * aspect) / size[0];
+                } else if (size[1] > 0.0) {
+                    scale[1] = (size[0] / aspect) / size[1];
+                }
+            }
+        }
+            break;
+        }
+    }
+
+    TRACE("%s dims %g,%g,%g", _dataSet->getName().c_str(),
+          size[0], size[1], size[2]);
+    TRACE("Setting scale to %g,%g,%g", scale[0], scale[1], scale[2]);
+    setScale(scale);
+}
+
+vtkAlgorithmOutput *HeightMap::initWarp(vtkAlgorithmOutput *input)
+{
+    TRACE("Warp scale: %g", _warpScale);
+    if (_warpScale == 0.0) {
+        _warp = NULL;
+        _normalsGenerator = NULL;
+        return input;
+    } else if (input == NULL) {
+        ERROR("NULL input");
+        return input;
+    } else {
+        if (_warp == NULL)
+            _warp = vtkSmartPointer<vtkWarpScalar>::New();
+        if (_normalsGenerator == NULL)
+            _normalsGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
+        switch (_sliceAxis) {
+        case X_AXIS:
+            _warp->SetNormal(1, 0, 0);
+            break;
+        case Y_AXIS:
+            _warp->SetNormal(0, 1, 0);
+            break;
+        default:
+            _warp->SetNormal(0, 0, 1);
+        }
+        _warp->UseNormalOn();
+        _warp->SetScaleFactor(_warpScale * _dataScale);
+        _warp->SetInputConnection(input);
+        _normalsGenerator->SetInputConnection(_warp->GetOutputPort());
+        _normalsGenerator->SetFeatureAngle(90.);
+        _normalsGenerator->AutoOrientNormalsOff();
+        _normalsGenerator->ComputePointNormalsOn();
+        return _normalsGenerator->GetOutputPort();
+    }
+}
+
+/**
+ * \brief Controls relative scaling of height of mountain plot
+ */
+void HeightMap::setHeightScale(double scale)
+{
+    if (_warpScale == scale)
+        return;
+
+    _warpScale = scale;
+    if (_warp == NULL && scale != 0.0) {
+        vtkAlgorithmOutput *warpOutput = initWarp(_mapper->GetInputConnection(0, 0));
+        _mapper->SetInputConnection(warpOutput);
+        _contourFilter->SetInputConnection(warpOutput);
+    } else if (scale == 0.0) {
+        vtkAlgorithmOutput *warpInput = _warp->GetInputConnection(0, 0);
+        _mapper->SetInputConnection(warpInput);
+        _contourFilter->SetInputConnection(warpInput);
+        _warp = NULL;
+    } else {
+        _warp->SetScaleFactor(_warpScale * _dataScale);
+    }
+#ifdef TRANSLATE_TO_ZORIGIN
+    double pos[3];
+    pos[0] = 0;
+    pos[1] = 0;
+    pos[2] = - _warpScale * _dataScale * _dataRange[0];
+    setPosition(pos);
+    TRACE("Z_POS: %g", pos[2]);
+#endif
+    if (_mapper != NULL)
+        _mapper->Update();
+    if (_contourMapper != NULL)
+        _contourMapper->Update();
+}
+
+/**
+ * \brief Select a 2D slice plane from a 3D DataSet
+ *
+ * \param[in] axis Axis of slice plane
+ * \param[in] ratio Position [0,1] of slice plane along axis
+ */
+void HeightMap::selectVolumeSlice(Axis axis, double ratio)
+{
+    if (_dataSet->is2D()) {
+        WARN("DataSet not 3D, returning");
+        return;
+    }
+
+    if (_volumeSlicer == NULL &&
+        _cutPlane == NULL) {
+        WARN("Called before update() or DataSet is not a volume");
+        return;
+    }
+
+    _sliceAxis = axis;
+    if (_warp != NULL) {
+        switch (axis) {
+        case X_AXIS:
+            _warp->SetNormal(1, 0, 0);
+            break;
+        case Y_AXIS:
+            _warp->SetNormal(0, 1, 0);
+            break;
+        case Z_AXIS:
+            _warp->SetNormal(0, 0, 1);
+            break;
+        default:
+            ERROR("Invalid Axis");
+            return;
+        }
+    }
+
+    if (_cutPlane != NULL) {
+        double bounds[6];
+        _dataSet->getBounds(bounds);
+        switch (axis) {
+        case X_AXIS:
+            _cutPlane->SetNormal(1, 0, 0);
+            _cutPlane->SetOrigin(bounds[0] + (bounds[1]-bounds[0]) * ratio,
+                                 0,
+                                 0);
+            break;
+        case Y_AXIS:
+            _cutPlane->SetNormal(0, 1, 0);
+            _cutPlane->SetOrigin(0,
+                                 bounds[2] + (bounds[3]-bounds[2]) * ratio,
+                                 0);
+            break;
+        case Z_AXIS:
+            _cutPlane->SetNormal(0, 0, 1);
+            _cutPlane->SetOrigin(0,
+                                 0,
+                                 bounds[4] + (bounds[5]-bounds[4]) * ratio);
+            break;
+        default:
+            ERROR("Invalid Axis");
+            return;
+        }
+    } else {
+        int dims[3];
+        if (_splatter != NULL) {
+            _splatter->GetSampleDimensions(dims);
+        } else {
+            vtkImageData *imageData = vtkImageData::SafeDownCast(_dataSet->getVtkDataSet());
+            if (imageData == NULL) {
+                ERROR("Not a volume data set");
+                return;
+            }
+            imageData->GetDimensions(dims);
+        }
+        int voi[6];
+
+        switch (axis) {
+        case X_AXIS:
+            voi[0] = voi[1] = (int)((dims[0]-1) * ratio);
+            voi[2] = 0;
+            voi[3] = dims[1]-1;
+            voi[4] = 0;
+            voi[5] = dims[2]-1;
+            break;
+        case Y_AXIS:
+            voi[0] = 0;
+            voi[1] = dims[0]-1;
+            voi[2] = voi[3] = (int)((dims[1]-1) * ratio);
+            voi[4] = 0;
+            voi[5] = dims[2]-1;
+            break;
+        case Z_AXIS:
+            voi[0] = 0;
+            voi[1] = dims[0]-1;
+            voi[2] = 0;
+            voi[3] = dims[1]-1;
+            voi[4] = voi[5] = (int)((dims[2]-1) * ratio);
+            break;
+        default:
+            ERROR("Invalid Axis");
+            return;
+        }
+
+        _volumeSlicer->SetVOI(voi);
+    }
+
+    if (_mapper != NULL)
+        _mapper->Update();
+    if (_contourMapper != NULL)
+        _contourMapper->Update();
+}
+
+void HeightMap::updateRanges(Renderer *renderer)
+{
+    TRACE("Enter");
+
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        const char *activeVectors = _dataSet->getActiveVectorsName();
+        if (activeVectors != NULL) {
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             activeVectors,
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 activeVectors,
+                                                 3, i);
+            }
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+ 
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+
+    if ((_contours.empty() && _numContours > 0) ||
+        _warpScale > 0.0) {
+        // Contour isovalues and/or warp need to be recomputed
+        update();
+    } else {
+        computeDataScale();
+    }
+
+    TRACE("Leave");
+}
+
+void HeightMap::setContourLineColorMapEnabled(bool mode)
+{
+    _contourColorMap = mode;
+
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+}
+
+void HeightMap::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void HeightMap::setColorMode(ColorMode mode,
+                             const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void HeightMap::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                             const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _mapper == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        _mapper->SetScalarModeToUsePointFieldData();
+        _contourMapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        _mapper->SetScalarModeToUseCellFieldData();
+        _contourMapper->SetScalarModeToUseCellFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (_splatter != NULL) {
+        if (name != NULL && strlen(name) > 0) {
+            _splatter->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, name);
+        }
+        _mapper->SelectColorArray("SplatterValues");
+        _contourMapper->SelectColorArray("SplatterValues");
+    } else if (name != NULL && strlen(name) > 0) {
+        _mapper->SelectColorArray(name);
+        _contourMapper->SelectColorArray(name);
+    } else {
+        _mapper->SetScalarModeToDefault();
+        _contourMapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+            TRACE("range %g, %g", range[0], range[1]);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+            TRACE("%s range %g, %g", name, r[0], r[1]);
+        }
+        _lut->Modified();
+    }
+
+    if (_contourColorMap) {
+        _contourMapper->ScalarVisibilityOn();
+    } else {
+        _contourMapper->ScalarVisibilityOff();
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _mapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _mapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void HeightMap::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void HeightMap::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_mapper != NULL) {
+            _mapper->UseLookupTableScalarRangeOn();
+            _mapper->SetLookupTable(_lut);
+        }
+        if (_contourMapper != NULL) {
+            _contourMapper->UseLookupTableScalarRangeOn();
+            _contourMapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Specify number of evenly spaced contour lines to render
+ *
+ * Will override any existing contours
+ */
+void HeightMap::setNumContours(int numContours)
+{
+    _contours.clear();
+    _numContours = numContours;
+
+    update();
+}
+
+/**
+ * \brief Specify an explicit list of contour isovalues
+ *
+ * Will override any existing contours
+ */
+void HeightMap::setContourList(const std::vector<double>& contours)
+{
+    _contours = contours;
+    _numContours = (int)_contours.size();
+
+    update();
+}
+
+/**
+ * \brief Get the number of contours
+ */
+int HeightMap::getNumContours() const
+{
+    return _numContours;
+}
+
+/**
+ * \brief Get the contour list (may be empty if number of contours
+ * was specified in place of a list)
+ */
+const std::vector<double>& HeightMap::getContourList() const
+{
+    return _contours;
+}
+
+/**
+ * \brief Turn on/off lighting of this object
+ */
+void HeightMap::setLighting(bool state)
+{
+    _lighting = state;
+    if (_dsActor != NULL)
+        _dsActor->GetProperty()->SetLighting((state ? 1 : 0));
+}
+
+/**
+ * \brief Turn on/off rendering of mesh edges
+ */
+void HeightMap::setEdgeVisibility(bool state)
+{
+    if (_dsActor != NULL) {
+        _dsActor->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of colormaped surface
+ */
+void HeightMap::setContourSurfaceVisibility(bool state)
+{
+    if (_dsActor != NULL) {
+        _dsActor->SetVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of contour isolines
+ */
+void HeightMap::setContourLineVisibility(bool state)
+{
+    if (_contourActor != NULL) {
+        _contourActor->SetVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Set RGB color of mesh
+ */
+void HeightMap::setColor(float color[3])
+{
+    _color[0] = color[0];
+    _color[1] = color[1];
+    _color[2] = color[2];
+    if (_dsActor != NULL)
+        _dsActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
+}
+
+/**
+ * \brief Set RGB color of polygon edges
+ */
+void HeightMap::setEdgeColor(float color[3])
+{
+    _edgeColor[0] = color[0];
+    _edgeColor[1] = color[1];
+    _edgeColor[2] = color[2];
+    if (_dsActor != NULL)
+        _dsActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+}
+
+/**
+ * \brief Set pixel width of polygon edges (may be a no-op)
+ */
+void HeightMap::setEdgeWidth(float edgeWidth)
+{
+    _edgeWidth = edgeWidth;
+    if (_dsActor != NULL)
+        _dsActor->GetProperty()->SetLineWidth(_edgeWidth);
+}
+
+/**
+ * \brief Set RGB color of contour isolines
+ */
+void HeightMap::setContourEdgeColor(float color[3])
+{
+    _contourEdgeColor[0] = color[0];
+    _contourEdgeColor[1] = color[1];
+    _contourEdgeColor[2] = color[2];
+    if (_contourActor != NULL) {
+        _contourActor->GetProperty()->SetColor(_contourEdgeColor[0],
+                                               _contourEdgeColor[1],
+                                               _contourEdgeColor[2]);
+        _contourActor->GetProperty()->SetEdgeColor(_contourEdgeColor[0],
+                                                   _contourEdgeColor[1],
+                                                   _contourEdgeColor[2]);
+    }
+}
+
+/**
+ * \brief Set pixel width of contour isolines (may be a no-op)
+ */
+void HeightMap::setContourEdgeWidth(float edgeWidth)
+{
+    _contourEdgeWidth = edgeWidth;
+    if (_contourActor != NULL)
+        _contourActor->GetProperty()->SetLineWidth(_contourEdgeWidth);
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void HeightMap::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_mapper != NULL) {
+        _mapper->SetClippingPlanes(planes);
+    }
+    if (_contourMapper != NULL) {
+        _contourMapper->SetClippingPlanes(planes);
+    }
+}
+
Index: vtkvis/tags/1.8.4/HeightMap.h
===================================================================
--- vtkvis/tags/1.8.4/HeightMap.h	(revision 6184)
+++ vtkvis/tags/1.8.4/HeightMap.h	(revision 6184)
@@ -0,0 +1,179 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_HEIGHTMAP_H
+#define VTKVIS_HEIGHTMAP_H
+
+#include <vtkSmartPointer.h>
+#include <vtkAlgorithmOutput.h>
+#include <vtkContourFilter.h>
+#include <vtkLookupTable.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkPlaneCollection.h>
+#include <vtkGaussianSplatter.h>
+#include <vtkExtractVOI.h>
+#include <vtkWarpScalar.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkAssembly.h>
+#include <vtkDataSet.h>
+#include <vtkPlane.h>
+
+#include <vector>
+
+#include "ColorMap.h"
+#include "Types.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Color-mapped plot of data set
+ */
+class HeightMap : public GraphicsObject {
+public:
+    enum CloudStyle {
+        CLOUD_MESH,
+        CLOUD_SPLAT
+    };
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+
+    HeightMap(int numContours, double heightScale = 1.0);
+
+    HeightMap(const std::vector<double>& contours, double heightScale = 1.0);
+
+    virtual ~HeightMap();
+
+    virtual const char *getClassName() const
+    {
+        return "HeightMap";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setLighting(bool state);
+
+    virtual void setColor(float color[3]);
+
+    virtual void setEdgeVisibility(bool state);
+
+    virtual void setEdgeColor(float color[3]);
+
+    virtual void setEdgeWidth(float edgeWidth);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    virtual void setAspect(double aspect);
+
+    void setCloudStyle(CloudStyle style);
+
+    void selectVolumeSlice(Axis axis, double ratio);
+
+    void setHeightScale(double scale);
+
+    double getHeightScale()
+    {
+        return _warpScale;
+    }
+
+    void setInterpolateBeforeMapping(bool state);
+
+    void setNumContours(int numContours);
+
+    void setContourList(const std::vector<double>& contours);
+
+    int getNumContours() const;
+
+    const std::vector<double>& getContourList() const;
+
+    void setContourLineColorMapEnabled(bool mode);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+    void setContourLineVisibility(bool state);
+
+    void setContourSurfaceVisibility(bool state);
+
+    void setContourEdgeColor(float color[3]);
+
+    void setContourEdgeWidth(float edgeWidth);
+
+private:
+    HeightMap();
+
+    virtual void initProp();
+    virtual void update();
+
+    void computeDataScale();
+
+    vtkAlgorithmOutput *initWarp(vtkAlgorithmOutput *input);
+
+    int _numContours;
+    std::vector<double> _contours;
+
+    bool _contourColorMap;
+    float _contourEdgeColor[3];
+    float _contourEdgeWidth;
+    double _warpScale;
+    double _dataScale;
+    Axis _sliceAxis;
+    bool _pipelineInitialized;
+
+    CloudStyle _cloudStyle;
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkPolyDataMapper> _mapper;
+    vtkSmartPointer<vtkContourFilter> _contourFilter;
+    vtkSmartPointer<vtkPolyDataMapper> _contourMapper;
+    vtkSmartPointer<vtkGaussianSplatter> _splatter;
+    vtkSmartPointer<vtkExtractVOI> _volumeSlicer;
+    vtkSmartPointer<vtkPlane> _cutPlane;
+    vtkSmartPointer<vtkWarpScalar> _warp;
+    vtkSmartPointer<vtkPolyDataNormals> _normalsGenerator;
+    vtkSmartPointer<vtkActor> _dsActor;
+    vtkSmartPointer<vtkActor> _contourActor;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Image.cpp
===================================================================
--- vtkvis/tags/1.8.4/Image.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Image.cpp	(revision 6184)
@@ -0,0 +1,285 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkSmartPointer.h>
+#include <vtkImageData.h>
+#include <vtkImageActor.h>
+#include <vtkImageSlice.h>
+#include <vtkImageProperty.h>
+#include <vtkImageMapper3D.h>
+#include <vtkImageResliceMapper.h>
+#include <vtkLookupTable.h>
+
+#include "Image.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+#define USE_RESLICE_MAPPER
+
+Image::Image() :
+    GraphicsObject(),
+    _colorMap(NULL)
+{
+}
+
+Image::~Image()
+{
+    TRACE("Deleting Image");
+}
+
+void Image::initProp()
+{
+    if (_prop == NULL) {
+#ifdef USE_RESLICE_MAPPER
+        _prop = vtkSmartPointer<vtkImageSlice>::New();
+#else
+        _prop = vtkSmartPointer<vtkImageActor>::New();
+#endif
+        vtkImageProperty *property = getImageProperty();
+        property->SetInterpolationTypeToLinear();
+        property->SetBackingColor(_color[0], _color[1], _color[2]);
+        property->BackingOff();
+        if (_dataSet != NULL)
+            _opacity = _dataSet->getOpacity();
+        property->SetOpacity(_opacity);
+
+        if (_dataSet != NULL)
+            setVisibility(_dataSet->getVisibility());
+    }
+}
+
+void Image::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    TRACE("DataSet: %s", _dataSet->getName().c_str());
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+    vtkImageData *imageData = vtkImageData::SafeDownCast(ds);
+
+    if (imageData == NULL) {
+        ERROR("DataSet is not an image.");
+        return;
+    }
+
+    initProp();
+
+    vtkImageActor *actor = getImageActor();
+    vtkImageMapper3D *mapper = getImageMapper();
+    if (mapper == NULL) {
+        TRACE("Creating mapper");
+        vtkSmartPointer<vtkImageResliceMapper> newMapper = vtkSmartPointer<vtkImageResliceMapper>::New();
+        newMapper->AutoAdjustImageQualityOff();
+        getImageSlice()->SetMapper(newMapper);
+        mapper = getImageMapper();
+        assert(mapper != NULL);
+    }
+    if (actor != NULL) {
+        TRACE("Have actor");
+        actor->SetInputData(imageData);
+        actor->InterpolateOn();
+    } else {
+        TRACE("No actor");
+        mapper->SetInputData(imageData);
+    }
+
+    mapper->SliceAtFocalPointOn();
+    mapper->SliceFacesCameraOn();
+
+    vtkImageResliceMapper *resliceMapper = getImageResliceMapper();
+    if (resliceMapper) {
+        TRACE("Mapper is a vtkImageResliceMapper");
+        resliceMapper->AutoAdjustImageQualityOff();
+        resliceMapper->ResampleToScreenPixelsOff();
+    } else {
+        TRACE("Mapper is a %s", mapper->GetClassName());
+    }
+
+    mapper->Update();
+
+    TRACE("Window: %g Level: %g", getWindow(), getLevel());
+}
+
+void Image::setColor(float color[3])
+{
+    GraphicsObject::setColor(color);
+
+    if (getImageProperty() != NULL) {
+        getImageProperty()->SetBackingColor(color[0], color[1], color[2]);
+    }
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Image::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL) {
+        _colorMap = NULL;
+        _lut = NULL;
+        if (getImageProperty() != NULL) {
+            getImageProperty()->SetLookupTable(NULL);
+        }
+        return;
+    }
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (getImageProperty() != NULL) {
+            getImageProperty()->UseLookupTableScalarRangeOn();
+            getImageProperty()->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(_dataRange);
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+}
+
+void Image::setAspect(double aspect)
+{
+    double bounds[6];
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+    ds->GetBounds(bounds);
+    double size[3];
+    size[0] = bounds[1] - bounds[0];
+    size[1] = bounds[3] - bounds[2];
+    size[2] = bounds[5] - bounds[4];
+    double scale[3];
+    scale[0] = scale[1] = scale[2] = 1.;
+
+    vtkImageMapper3D *mapper = getImageMapper();
+    if (mapper == NULL)
+        return;
+
+    mapper->Update();
+
+    double normal[3];
+    mapper->GetSlicePlane()->GetNormal(normal);
+
+    Axis sliceAxis = Z_AXIS;
+    if (fabs(normal[0]) == 1.0 &&
+        normal[1] == 0.0 &&
+        normal[2] == 0.0) {
+        sliceAxis = X_AXIS;
+    } else if (normal[0] == 0.0 &&
+               fabs(normal[1]) == 1.0 &&
+               normal[2] == 0.0) {
+        sliceAxis = Y_AXIS;
+    } else if (normal[0] == 0.0 &&
+               normal[1] == 0.0 &&
+               fabs(normal[2]) == 1.0) {
+        sliceAxis = Z_AXIS;
+    } else {
+        TRACE("Non orthogonal slice plane, setting native aspect");
+        aspect = 0.0;
+    }
+
+    if (aspect == 1.0) {
+        // Square
+        switch (sliceAxis) {
+        case X_AXIS: {
+            if (size[1] > size[2] && size[2] > 0.0) {
+                scale[2] = size[1] / size[2];
+            } else if (size[2] > size[1] && size[1] > 0.0) {
+                scale[1] = size[2] / size[1];
+            }
+        }
+            break;
+        case Y_AXIS: {
+            if (size[0] > size[2] && size[2] > 0.0) {
+                scale[2] = size[0] / size[2];
+            } else if (size[2] > size[0] && size[0] > 0.0) {
+                scale[0] = size[2] / size[0];
+            }
+        }
+            break;
+        case Z_AXIS: {
+            if (size[0] > size[1] && size[1] > 0.0) {
+                scale[1] = size[0] / size[1];
+            } else if (size[1] > size[0] && size[0] > 0.0) {
+                scale[0] = size[1] / size[0];
+            }
+        }
+            break;
+        }
+    } else if (aspect != 0.0) {
+        switch (sliceAxis) {
+        case X_AXIS: {
+            if (aspect > 1.0) {
+                if (size[2] > size[1] && size[1] > 0.0) {
+                    scale[1] = (size[2] / aspect) / size[1];
+                } else if (size[2] > 0.0) {
+                    scale[2] = (size[1] * aspect) / size[2];
+                }
+            } else {
+                if (size[1] > size[2] && size[2] > 0.0) {
+                    scale[2] = (size[1] * aspect) / size[2];
+                } else if (size[1] > 0.0) {
+                    scale[1] = (size[2] / aspect) / size[1];
+                }
+            }
+        }
+            break;
+        case Y_AXIS: {
+            if (aspect > 1.0) {
+                if (size[0] > size[2] && size[2] > 0.0) {
+                    scale[2] = (size[0] / aspect) / size[2];
+                } else if (size[0] > 0.0) {
+                    scale[0] = (size[2] * aspect) / size[0];
+                }
+            } else {
+                if (size[2] > size[0] && size[0] > 0.0) {
+                    scale[0] = (size[2] * aspect) / size[0];
+                } else if (size[2] > 0.0) {
+                    scale[2] = (size[0] / aspect) / size[2];
+                }
+            }
+        }
+            break;
+        case Z_AXIS: {
+            if (aspect > 1.0) {
+                if (size[0] > size[1] && size[1] > 0.0) {
+                    scale[1] = (size[0] / aspect) / size[1];
+                } else if (size[0] > 0.0) {
+                    scale[0] = (size[1] * aspect) / size[0];
+                }
+            } else {
+                if (size[1] > size[0] && size[0] > 0.0) {
+                    scale[0] = (size[1] * aspect) / size[0];
+                } else if (size[1] > 0.0) {
+                    scale[1] = (size[0] / aspect) / size[1];
+                }
+            }
+        }
+            break;
+        }
+    }
+
+    TRACE("%s dims %g,%g,%g", _dataSet->getName().c_str(),
+          size[0], size[1], size[2]);
+    TRACE("Setting scale to %g,%g,%g", scale[0], scale[1], scale[2]);
+    setScale(scale);
+    mapper->Modified();
+    mapper->Update();
+}
+
+void Image::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    vtkImageMapper3D *mapper = getImageMapper();
+    if (mapper != NULL) {
+        mapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Image.h
===================================================================
--- vtkvis/tags/1.8.4/Image.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Image.h	(revision 6184)
@@ -0,0 +1,265 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_IMAGE_H
+#define VTKVIS_IMAGE_H
+
+#include <vtkSmartPointer.h>
+#include <vtkImageSlice.h>
+#include <vtkImageActor.h>
+#include <vtkImageMapper3D.h>
+#include <vtkImageResliceMapper.h>
+#include <vtkImageSliceMapper.h>
+#include <vtkLookupTable.h>
+#include <vtkPlaneCollection.h>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+#include "DataSet.h"
+#include "Trace.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Image Slicer
+ *
+ */
+class Image : public GraphicsObject
+{
+public:
+    enum InterpType {
+        INTERP_NEAREST,
+        INTERP_LINEAR,
+        INTERP_CUBIC
+    };
+
+    Image();
+    virtual ~Image();
+
+    virtual const char *getClassName() const
+    {
+        return "Image";
+    }
+
+    virtual void initProp();
+
+    virtual void setColor(float color[3]);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    virtual void setAspect(double aspect);
+
+    void updateColorMap()
+    {
+        setColorMap(_colorMap);
+    }
+
+    void setColorMap(ColorMap *cmap);
+
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void setSlicePlane(double normal[3], double origin[3])
+    {
+        setSliceFollowsCamera(false);
+
+        vtkImageMapper3D *mapper = getImageMapper();
+        vtkImageResliceMapper *resliceMapper = vtkImageResliceMapper::SafeDownCast(mapper);
+        if (resliceMapper != NULL) {
+            vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
+            plane->SetNormal(normal);
+            plane->SetOrigin(origin);
+            resliceMapper->SetSlicePlane(plane);
+        }
+    }
+
+    void setSliceFollowsCamera(bool state)
+    {
+        vtkImageMapper3D *mapper = getImageMapper();
+        if (mapper != NULL) {
+            mapper->SetSliceFacesCamera(state ? 1 : 0);
+            mapper->SetSliceAtFocalPoint(state ? 1 : 0);
+        }
+    }
+
+    void setJumpToNearestSlice(bool state)
+    {
+        vtkImageMapper3D *mapper = getImageMapper();
+        vtkImageResliceMapper *resliceMapper = vtkImageResliceMapper::SafeDownCast(mapper);
+        if (resliceMapper != NULL) {
+            resliceMapper->SetJumpToNearestSlice(state ? 1 : 0);
+        }
+    }
+
+    void setExtents(int extent[6])
+    {
+        vtkImageActor *actor = getImageActor();
+        if (actor == NULL)
+            return;
+
+        actor->SetDisplayExtent(extent);
+    }
+
+    void setZSlice(int z)
+    {
+        vtkImageActor *actor = getImageActor();
+        if (actor == NULL)
+            return;
+
+        TRACE("before slice # %d, (%d,%d), whole z: (%d,%d)",
+              actor->GetSliceNumber(), actor->GetSliceNumberMin(), actor->GetSliceNumberMax(),
+              actor->GetZSlice(), actor->GetWholeZMin(), actor->GetWholeZMax());
+
+        actor->SetZSlice(z);
+
+        TRACE("after slice # %d, (%d,%d), whole z: (%d,%d)",
+              actor->GetSliceNumber(), actor->GetSliceNumberMin(), actor->GetSliceNumberMax(),
+              actor->GetZSlice(), actor->GetWholeZMin(), actor->GetWholeZMax());
+    }
+
+    void setUseWindowLevel(bool state)
+    {
+        vtkImageProperty *property = getImageProperty();
+        if (property == NULL)
+            return;
+
+        property->SetUseLookupTableScalarRange((state ? 0 : 1));
+    }
+
+    double getWindow()
+    {
+        vtkImageProperty *property = getImageProperty();
+        if (property == NULL)
+            return 0.0;
+
+        return property->GetColorWindow();
+    }
+
+    void setWindow(double window)
+    {
+        vtkImageProperty *property = getImageProperty();
+        if (property == NULL)
+            return;
+
+        property->SetColorWindow(window);
+        property->UseLookupTableScalarRangeOff();
+    }
+
+    double getLevel()
+    {
+        vtkImageProperty *property = getImageProperty();
+        if (property == NULL)
+            return 0.0;
+
+        return property->GetColorLevel();
+    }
+
+    void setLevel(double level)
+    {
+        vtkImageProperty *property = getImageProperty();
+        if (property == NULL)
+            return;
+
+        property->SetColorLevel(level);
+        property->UseLookupTableScalarRangeOff();
+    }
+
+    void setBacking(bool state)
+    {
+        vtkImageProperty *property = getImageProperty();
+        if (property == NULL)
+            return;
+
+        property->SetBacking((state ? 1 : 0));
+    }
+
+    void setBorder(bool state)
+    {
+        vtkImageMapper3D *mapper = getImageMapper();
+        if (mapper == NULL)
+            return;
+
+        mapper->SetBorder((state ? 1 : 0));
+    }
+
+    void setBackground(bool state)
+    {
+        vtkImageMapper3D *mapper = getImageMapper();
+        if (mapper == NULL)
+            return;
+
+        mapper->SetBackground((state ? 1 : 0));
+    }
+
+    void setInterpolationType(InterpType type)
+    {
+        vtkImageProperty *property = getImageProperty();
+        if (property == NULL)
+            return;
+
+        switch (type) {
+        case INTERP_NEAREST:
+            property->SetInterpolationTypeToNearest();
+            break;
+        case INTERP_LINEAR:
+            property->SetInterpolationTypeToLinear();
+            break;
+        case INTERP_CUBIC:
+            property->SetInterpolationTypeToCubic();
+            break;
+        }
+    }
+
+private:
+    virtual void update();
+
+    vtkImageProperty *getImageProperty()
+    {
+        if (getImageSlice() != NULL) {
+            return getImageSlice()->GetProperty();
+        } else {
+            return NULL;
+        }
+    }
+
+    vtkImageMapper3D *getImageMapper()
+    {
+        if (getImageSlice() != NULL) {
+            return getImageSlice()->GetMapper();
+        } else {
+            return NULL;
+        }
+    }
+
+    vtkImageResliceMapper *getImageResliceMapper()
+    {
+        if (getImageSlice() != NULL) {
+            return vtkImageResliceMapper::SafeDownCast(getImageSlice()->GetMapper());
+        } else {
+            return NULL;
+        }
+    }
+
+    vtkImageSliceMapper *getImageSliceMapper()
+    {
+        if (getImageSlice() != NULL) {
+            return vtkImageSliceMapper::SafeDownCast(getImageSlice()->GetMapper());
+        } else {
+            return NULL;
+        }
+    }
+
+    ColorMap *_colorMap;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/ImageCutplane.cpp
===================================================================
--- vtkvis/tags/1.8.4/ImageCutplane.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/ImageCutplane.cpp	(revision 6184)
@@ -0,0 +1,531 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+#include <cfloat>
+#include <cstring>
+
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkRectilinearGrid.h>
+#include <vtkStructuredGrid.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkProperty.h>
+#include <vtkImageData.h>
+#include <vtkProbeFilter.h>
+#include <vtkGaussianSplatter.h>
+#include <vtkLookupTable.h>
+#include <vtkTransform.h>
+#include <vtkExtractVOI.h>
+#include <vtkDataSetSurfaceFilter.h>
+
+#include "ImageCutplane.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+ImageCutplane::ImageCutplane() :
+    GraphicsObject(),
+    _pipelineInitialized(false),
+    _colorMap(NULL),
+    _renderer(NULL)
+{
+}
+
+ImageCutplane::~ImageCutplane()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting ImageCutplane for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting ImageCutplane with NULL DataSet");
+#endif
+}
+
+/**
+ * \brief Create and initialize a VTK Prop to render the object
+ */
+void ImageCutplane::initProp()
+{
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkAssembly>::New();
+
+        for (int i = 0; i < 3; i++) {
+            _actor[i] = vtkSmartPointer<vtkImageSlice>::New();
+            _borderActor[i] = vtkSmartPointer<vtkActor>::New();
+            vtkImageProperty *imgProperty = _actor[i]->GetProperty();
+            imgProperty->SetInterpolationTypeToLinear();
+            imgProperty->SetBackingColor(_color[0], _color[1], _color[2]);
+            imgProperty->BackingOff();
+            imgProperty->SetOpacity(_opacity);
+            imgProperty->SetAmbient(0.0);
+            imgProperty->SetDiffuse(1.0);
+            vtkProperty *property = _borderActor[i]->GetProperty();
+            property->SetColor(_color[0], _color[1], _color[2]);
+            property->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+            property->SetLineWidth(_edgeWidth);
+            property->SetPointSize(_pointSize);
+            property->EdgeVisibilityOff();
+            property->SetOpacity(_opacity);
+            property->LightingOff();
+            
+            getAssembly()->AddPart(_borderActor[i]);
+        }
+    } else {
+        for (int i = 0; i < 3; i++) {
+            getAssembly()->RemovePart(_actor[i]);
+        }
+    }
+}
+
+void ImageCutplane::setInterpolationType(InterpType type)
+{
+    for (int i = 0; i < 3; i++) {
+        switch (type) {
+        case INTERP_NEAREST:
+            _actor[i]->GetProperty()->SetInterpolationTypeToNearest();
+            break;
+        case INTERP_LINEAR:
+            _actor[i]->GetProperty()->SetInterpolationTypeToLinear();
+            break;
+        case INTERP_CUBIC:
+            _actor[i]->GetProperty()->SetInterpolationTypeToCubic();
+            break;
+        }
+    }
+}
+
+/**
+ * \brief Set the material color (sets ambient, diffuse, and specular)
+ */
+void ImageCutplane::setColor(float color[3])
+{
+    GraphicsObject::setColor(color);
+
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->SetBackingColor(color[0], color[1], color[2]);
+        }
+        if (_borderActor[i] != NULL) {
+            _borderActor[i]->GetProperty()->SetColor(color[0], color[1], color[2]);
+        }
+    }
+}
+
+void ImageCutplane::setAmbient(double ambient)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->SetAmbient(ambient);
+        }
+    }
+}
+
+void ImageCutplane::setDiffuse(double diffuse)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->SetDiffuse(diffuse);
+        }
+    }
+}
+
+void ImageCutplane::setOpacity(double opacity)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->SetOpacity(opacity);
+        }
+    }
+}
+
+void ImageCutplane::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    if (_dataSet->is2D()) {
+        USER_ERROR("Image cutplane requires a 3D data set");
+        _dataSet = NULL;
+        return;
+    }
+
+    if (ds->GetPointData() == NULL ||
+        ds->GetPointData()->GetScalars() == NULL) {
+        USER_ERROR("No scalar field was found in the data set");
+        return;
+    }
+
+    double bounds[6];
+    _dataSet->getBounds(bounds);
+    // Mapper, actor to render color-mapped data set
+    for (int i = 0; i < 3; i++) {
+        if (_cutPlane[i] == NULL) {
+            _cutPlane[i] = vtkSmartPointer<vtkPlane>::New();
+            switch (i) {
+            case 0:
+                _cutPlane[i]->SetNormal(1, 0, 0);
+                _cutPlane[i]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])/2.,
+                                        0,
+                                        0);
+                break;
+            case 1:
+                _cutPlane[i]->SetNormal(0, 1, 0);
+                _cutPlane[i]->SetOrigin(0,
+                                        bounds[2] + (bounds[3]-bounds[2])/2.,
+                                        0);
+                break;
+            case 2:
+            default:
+                _cutPlane[i]->SetNormal(0, 0, 1);
+                _cutPlane[i]->SetOrigin(0,
+                                        0,
+                                        bounds[4] + (bounds[5]-bounds[4])/2.);
+                break;
+            }
+        }
+        if (_mapper[i] == NULL) {
+            _mapper[i] = vtkSmartPointer<vtkImageResliceMapper>::New();
+            _mapper[i]->SetSlicePlane(_cutPlane[i]);
+        }
+    }
+
+    if (!_pipelineInitialized) {
+        vtkImageData *imgData = vtkImageData::SafeDownCast(ds);
+        if (imgData == NULL) {
+            // Need to resample to ImageData
+            if (_dataSet->isCloud()) {
+                // DataSet is a 3D point cloud
+                vtkSmartPointer<vtkGaussianSplatter> splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                splatter->SetInputData(ds);
+                int dims[3];
+                dims[0] = dims[1] = dims[2] = 64;
+                if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
+                    vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
+                } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
+                    vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
+                }
+                TRACE("Generating volume with dims (%d,%d,%d) from %d points",
+                      dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
+                splatter->SetSampleDimensions(dims);
+                splatter->Update();
+
+                TRACE("Done generating volume");
+
+                for (int i = 0; i < 3; i++) {
+                    _mapper[i]->SetInputConnection(splatter->GetOutputPort());
+                }
+            } else {
+                // (Slow) Resample using ProbeFilter
+                double xLen = bounds[1] - bounds[0];
+                double yLen = bounds[3] - bounds[2];
+                double zLen = bounds[5] - bounds[4];
+
+                int dims[3];
+                dims[0] = dims[1] = dims[2] = 64;
+                if (xLen == 0.0) dims[0] = 1;
+                if (yLen == 0.0) dims[1] = 1;
+                if (zLen == 0.0) dims[2] = 1;
+                if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
+                    vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
+                } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
+                    vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
+                }
+                TRACE("Generating volume with dims (%d,%d,%d) from %d points",
+                      dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
+
+                double xSpacing = (dims[0] == 1 ? 0.0 : xLen/((double)(dims[0]-1)));
+                double ySpacing = (dims[1] == 1 ? 0.0 : yLen/((double)(dims[1]-1)));
+                double zSpacing = (dims[2] == 1 ? 0.0 : zLen/((double)(dims[2]-1)));
+
+                vtkSmartPointer<vtkImageData> resampleGrid = vtkSmartPointer<vtkImageData>::New();
+                resampleGrid->SetDimensions(dims[0], dims[1], dims[2]);
+                resampleGrid->SetOrigin(bounds[0], bounds[2], bounds[4]);
+                resampleGrid->SetSpacing(xSpacing, ySpacing, zSpacing); 
+
+                vtkSmartPointer<vtkProbeFilter> probe = vtkSmartPointer<vtkProbeFilter>::New();
+                probe->SetInputData(resampleGrid);
+                probe->SetSourceData(ds);
+                probe->Update();
+
+                TRACE("Done generating volume");
+
+                for (int i = 0; i < 3; i++) {
+                    _mapper[i]->SetInputConnection(probe->GetOutputPort());
+                }
+            }
+        } else {
+            // Have ImageData
+            for (int i = 0; i < 3; i++) {
+                _mapper[i]->SetInputData(imgData);
+            }
+        }
+    }
+
+    initProp();
+
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] != NULL && _borderMapper[i] == NULL) {
+            _borderMapper[i] = vtkSmartPointer<vtkPolyDataMapper>::New();
+            _outlineSource[i] = vtkSmartPointer<vtkOutlineSource>::New();
+            switch (i) {
+            case 0:
+                _outlineSource[i]->SetBounds(bounds[0] + (bounds[1]-bounds[0])/2.,
+                                             bounds[0] + (bounds[1]-bounds[0])/2.,
+                                             bounds[2], bounds[3],
+                                             bounds[4], bounds[5]);
+                break;
+            case 1:
+                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
+                                             bounds[2] + (bounds[3]-bounds[2])/2.,
+                                             bounds[2] + (bounds[3]-bounds[2])/2.,
+                                             bounds[4], bounds[5]);
+                break;
+            case 2:
+                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
+                                             bounds[2], bounds[3],
+                                             bounds[4] + (bounds[5]-bounds[4])/2.,
+                                             bounds[4] + (bounds[5]-bounds[4])/2.);
+                break;
+            default:
+                ;
+            }
+            _borderMapper[i]->SetInputConnection(_outlineSource[i]->GetOutputPort());
+            _borderMapper[i]->SetResolveCoincidentTopologyToPolygonOffset();
+        }
+    }
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+    }
+
+    _pipelineInitialized = true;
+
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] != NULL) {
+            _actor[i]->SetMapper(_mapper[i]);
+            vtkImageMapper3D *im3d = _mapper[i];
+            im3d->Update();
+        }
+        if (_borderMapper[i] != NULL) {
+            _borderActor[i]->SetMapper(_borderMapper[i]);
+            _borderMapper[i]->Update();
+        }
+        // Only add cutter actor to assembly if geometry was
+        // produced, in order to prevent messing up assembly bounds
+        double bounds[6];
+        _actor[i]->GetBounds(bounds);
+        if (bounds[0] <= bounds[1]) {
+            getAssembly()->AddPart(_actor[i]);
+        }
+    }
+}
+
+/**
+ * \brief Select a 2D slice plane from a 3D DataSet
+ *
+ * \param[in] axis Axis of slice plane
+ * \param[in] ratio Position [0,1] of slice plane along axis
+ */
+void ImageCutplane::selectVolumeSlice(Axis axis, double ratio)
+{
+    if (_dataSet->is2D()) {
+        WARN("DataSet not 3D, returning");
+        return;
+    }
+
+    if ((axis == X_AXIS &&_cutPlane[0] == NULL) ||
+        (axis == Y_AXIS &&_cutPlane[1] == NULL) ||
+        (axis == Z_AXIS &&_cutPlane[2] == NULL)) {
+        WARN("Called before update() or DataSet is not a volume");
+        return;
+    }
+
+    double bounds[6];
+    _dataSet->getBounds(bounds);
+#if 0
+    if (ratio == 0.0)
+        ratio = 0.001;
+    if (ratio == 1.0)
+        ratio = 0.999;
+#endif
+    switch (axis) {
+    case X_AXIS:
+        _cutPlane[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0]) * ratio,
+                                0,
+                                0);
+        _outlineSource[0]->SetBounds(bounds[0] + (bounds[1]-bounds[0]) * ratio,
+                                     bounds[0] + (bounds[1]-bounds[0]) * ratio,
+                                     bounds[2], bounds[3],
+                                     bounds[4], bounds[5]);
+        break;
+    case Y_AXIS:
+        _cutPlane[1]->SetOrigin(0,
+                                bounds[2] + (bounds[3]-bounds[2]) * ratio,
+                                0);
+        _outlineSource[1]->SetBounds(bounds[0], bounds[1],
+                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
+                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
+                                     bounds[4], bounds[5]);
+        break;
+    case Z_AXIS:
+        _cutPlane[2]->SetOrigin(0,
+                                0,
+                                bounds[4] + (bounds[5]-bounds[4]) * ratio);
+        _outlineSource[2]->SetBounds(bounds[0], bounds[1],
+                                     bounds[2], bounds[3],
+                                     bounds[4] + (bounds[5]-bounds[4]) * ratio,
+                                     bounds[4] + (bounds[5]-bounds[4]) * ratio);
+        break;
+    default:
+        ERROR("Invalid Axis");
+        return;
+    }
+    update();
+}
+
+void ImageCutplane::updateRanges(Renderer *renderer)
+{
+    GraphicsObject::updateRanges(renderer);
+
+    updateColorMap();
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void ImageCutplane::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void ImageCutplane::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL) {
+        _colorMap = NULL;
+        _lut = NULL;
+        for (int i = 0; i < 3; i++) {
+            if (_actor[i] != NULL) {
+                _actor[i]->GetProperty()->SetLookupTable(NULL);
+            }
+        }
+        return;
+    }
+
+    _colorMap = cmap;
+
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        for (int i = 0; i < 3; i++) {
+            if (_actor[i] != NULL) {
+                _actor[i]->GetProperty()->UseLookupTableScalarRangeOn();
+                _actor[i]->GetProperty()->SetLookupTable(_lut);
+            }
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(_dataRange);
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+}
+
+void ImageCutplane::setUseWindowLevel(bool state)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->SetUseLookupTableScalarRange((state ? 0 : 1));
+        }
+    }
+}
+
+void ImageCutplane::setWindow(double window)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->UseLookupTableScalarRangeOff();
+            _actor[i]->GetProperty()->SetColorWindow(window);
+        }
+    }
+}
+
+void ImageCutplane::setLevel(double level)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_actor[i] != NULL) {
+            _actor[i]->GetProperty()->UseLookupTableScalarRangeOff();
+            _actor[i]->GetProperty()->SetColorLevel(level);
+        }
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of outlines
+ */
+void ImageCutplane::setOutlineVisibility(bool state)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_borderActor[i] != NULL) {
+            _borderActor[i]->SetVisibility((state ? 1 : 0));
+        }
+    }
+}
+
+/**
+ * \brief Set visibility of cutplane on specified axis
+ */
+void ImageCutplane::setSliceVisibility(Axis axis, bool state)
+{
+    switch (axis) {
+    case X_AXIS:
+        if (_actor[0] != NULL)
+            _actor[0]->SetVisibility((state ? 1 : 0));
+        if (_borderActor[0] != NULL)
+            _borderActor[0]->SetVisibility((state ? 1 : 0));
+        break;
+    case Y_AXIS:
+        if (_actor[1] != NULL)
+            _actor[1]->SetVisibility((state ? 1 : 0));
+        if (_borderActor[1] != NULL)
+            _borderActor[1]->SetVisibility((state ? 1 : 0));
+        break;
+    case Z_AXIS:
+    default:
+        if (_actor[2] != NULL)
+            _actor[2]->SetVisibility((state ? 1 : 0));
+        if (_borderActor[2] != NULL)
+            _borderActor[2]->SetVisibility((state ? 1 : 0));
+        break;
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void ImageCutplane::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    for (int i = 0; i < 3; i++) {
+        if (_mapper[i] != NULL) {
+            _mapper[i]->SetClippingPlanes(planes);
+        }
+        if (_borderMapper[i] != NULL) {
+            _borderMapper[i]->SetClippingPlanes(planes);
+        }
+    }
+}
Index: vtkvis/tags/1.8.4/ImageCutplane.h
===================================================================
--- vtkvis/tags/1.8.4/ImageCutplane.h	(revision 6184)
+++ vtkvis/tags/1.8.4/ImageCutplane.h	(revision 6184)
@@ -0,0 +1,106 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_IMAGE_CUTPLANE_H
+#define VTKVIS_IMAGE_CUTPLANE_H
+
+#include <vtkSmartPointer.h>
+#include <vtkLookupTable.h>
+#include <vtkImageSlice.h>
+#include <vtkImageResliceMapper.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkPlaneCollection.h>
+#include <vtkPlane.h>
+#include <vtkOutlineSource.h>
+
+#include "ColorMap.h"
+#include "Types.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Color-mapped plot of orthogonal slices through a data set
+ * 
+ * The DataSet must be image data (3D uniform grid)
+ */
+class ImageCutplane : public GraphicsObject {
+public:
+    enum InterpType {
+        INTERP_NEAREST,
+        INTERP_LINEAR,
+        INTERP_CUBIC
+    };
+
+    ImageCutplane();
+    virtual ~ImageCutplane();
+
+    virtual const char *getClassName() const
+    {
+        return "ImageCutplane";
+    }
+
+    virtual void setColor(float color[3]);
+
+    virtual void setAmbient(double ambient);
+
+    virtual void setDiffuse(double diffuse);
+
+    virtual void setOpacity(double opacity);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setColorMap(ColorMap *colorMap);
+
+    void setUseWindowLevel(bool state);
+
+    void setWindow(double window);
+
+    void setLevel(double level);
+
+    void setOutlineVisibility(bool state);
+
+    void selectVolumeSlice(Axis axis, double ratio);
+
+    void setSliceVisibility(Axis axis, bool state);
+
+    void setInterpolationType(InterpType type);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    virtual void initProp();
+    virtual void update();
+
+    bool _pipelineInitialized;
+
+    ColorMap *_colorMap;
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkImageSlice> _actor[3];
+    vtkSmartPointer<vtkActor> _borderActor[3];
+    vtkSmartPointer<vtkImageResliceMapper> _mapper[3];
+    vtkSmartPointer<vtkPolyDataMapper> _borderMapper[3];
+    vtkSmartPointer<vtkPlane> _cutPlane[3];
+    vtkSmartPointer<vtkOutlineSource> _outlineSource[3];
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/LIC.cpp
===================================================================
--- vtkvis/tags/1.8.4/LIC.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/LIC.cpp	(revision 6184)
@@ -0,0 +1,482 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCellDataToPointData.h>
+#include <vtkImageData.h>
+#include <vtkPolyData.h>
+#include <vtkDataSetMapper.h>
+#include <vtkPainterPolyDataMapper.h>
+#include <vtkDataSetSurfaceFilter.h>
+#include <vtkCutter.h>
+#include <vtkImageMask.h>
+#include <vtkImageCast.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkTransform.h>
+
+#include "LIC.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+LIC::LIC() :
+    GraphicsObject(),
+    _sliceAxis(Z_AXIS),
+    _colorMap(NULL),
+    _resolution(128)
+{
+}
+
+LIC::~LIC()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting LIC for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting LIC with NULL DataSet");
+#endif
+}
+
+void LIC::initProp()
+{
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkActor>::New();
+        vtkProperty *property = getActor()->GetProperty();
+        property->SetOpacity(_opacity);
+        property->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+        property->SetLineWidth(_edgeWidth);
+        property->EdgeVisibilityOff();
+        property->SetAmbient(.2);
+        if (!_lighting)
+            property->LightingOff();
+    }
+}
+
+void LIC::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
+    if (ds->GetPointData() == NULL ||
+        ds->GetPointData()->GetVectors() == NULL) {
+         TRACE("No vector point data in dataset %s", _dataSet->getName().c_str());
+         if (ds->GetCellData() != NULL &&
+             ds->GetCellData()->GetVectors() != NULL) {
+             cellToPtData = 
+                 vtkSmartPointer<vtkCellDataToPointData>::New();
+             cellToPtData->SetInputData(ds);
+             //cellToPtData->PassCellDataOn();
+             cellToPtData->Update();
+             ds = cellToPtData->GetOutput();
+         } else {
+             USER_ERROR("No vector field was found in the data set");
+             return;
+         }
+    }
+
+#ifdef HAVE_LIC
+    vtkImageData *imageData = vtkImageData::SafeDownCast(ds);
+    if (imageData != NULL) {
+        if (_lic == NULL) {
+            _lic = vtkSmartPointer<vtkImageDataLIC2D>::New();
+        }
+        if (!_dataSet->is2D()) {
+            // 3D image/volume/uniform grid
+            if (_volumeSlicer == NULL)
+                _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
+            int dims[3];
+            imageData->GetDimensions(dims);
+            TRACE("Image data dimensions: %d %d %d", dims[0], dims[1], dims[2]);
+            _volumeSlicer->SetInputData(ds);
+            _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, (dims[2]-1)/2, (dims[2]-1)/2);
+            _volumeSlicer->SetSampleRate(1, 1, 1);
+            _lic->SetInputConnection(_volumeSlicer->GetOutputPort());
+        } else {
+            // 2D image/uniform grid
+            _lic->SetInputData(ds);
+        }
+        if (_mapper == NULL) {
+            _mapper = vtkSmartPointer<vtkDataSetMapper>::New();
+        }
+        _mapper->SetInputConnection(_lic->GetOutputPort());
+    } else if (vtkPolyData::SafeDownCast(ds) == NULL ||
+               _dataSet->isCloud() ||
+               _dataSet->is2D()) {
+        // structured/rectilinear/unstructured grid, cloud or 2D polydata
+        if (_lic == NULL) {
+            _lic = vtkSmartPointer<vtkImageDataLIC2D>::New();
+        }
+
+        // Need to convert to vtkImageData
+        double bounds[6];
+        ds->GetBounds(bounds);
+        double xSize = bounds[1] - bounds[0];
+        double ySize = bounds[3] - bounds[2];
+        double zSize = bounds[5] - bounds[4];
+        int minDir = 2;
+        if (xSize < ySize && xSize < zSize)
+            minDir = 0;
+        if (ySize < xSize && ySize < zSize)
+            minDir = 1;
+        if (_probeFilter == NULL)
+            _probeFilter = vtkSmartPointer<vtkProbeFilter>::New();
+
+        PrincipalPlane plane;
+        double offset;
+        if (!_dataSet->is2D(&plane, &offset)) {
+            // 3D Data: Sample a plane within the bounding box
+            vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
+            if (_cutPlane == NULL) {
+                _cutPlane = vtkSmartPointer<vtkPlane>::New();
+            }
+            if (minDir == 0) {
+                _cutPlane->SetNormal(1, 0, 0);
+                _cutPlane->SetOrigin(bounds[0] + (bounds[1]-bounds[0])/2.,
+                                     0,
+                                     0);
+            } else if (minDir == 1) {
+                _cutPlane->SetNormal(0, 1, 0);
+                _cutPlane->SetOrigin(0,
+                                     bounds[2] + (bounds[3]-bounds[2])/2.,
+                                     0);
+            } else {
+                _cutPlane->SetNormal(0, 0, 1);
+                _cutPlane->SetOrigin(0,
+                                     0,
+                                     bounds[4] + (bounds[5]-bounds[4])/2.);
+            }
+
+            if (_dataSet->isCloud()) {
+                // 3D cloud -- Need to mesh it before we can resample
+                vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+                mesher->SetInputData(ds);
+                cutter->SetInputConnection(mesher->GetOutputPort());
+            } else {
+                cutter->SetInputData(ds);
+            }
+            cutter->SetCutFunction(_cutPlane);
+            _probeFilter->SetSourceConnection(cutter->GetOutputPort());
+        } else {
+            if (_dataSet->isCloud()) {
+                // 2D cloud -- Need to mesh it before we can resample
+                vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                if (plane == PLANE_ZY) {
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->RotateWXYZ(90, 0, 1, 0);
+                    if (offset != 0.0) {
+                        trans->Translate(-offset, 0, 0);
+                    }
+                    mesher->SetTransform(trans);
+                } else if (plane == PLANE_XZ) {
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->RotateWXYZ(-90, 1, 0, 0);
+                    if (offset != 0.0) {
+                        trans->Translate(0, -offset, 0);
+                    }
+                    mesher->SetTransform(trans);
+                } else if (offset != 0.0) {
+                    // XY with Z offset
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->Translate(0, 0, -offset);
+                    mesher->SetTransform(trans);
+                }
+                mesher->SetInputData(ds);
+                _probeFilter->SetSourceConnection(mesher->GetOutputPort());
+            } else {
+                _probeFilter->SetSourceData(ds);
+            }
+        }
+
+        vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
+        int xdim, ydim, zdim;
+        double origin[3], spacing[3];
+        int res = _resolution;
+        if (minDir == 0) {
+            xdim = 1;
+            ydim = res;
+            zdim = res;
+            origin[0] = bounds[0] + xSize/2.;
+            origin[1] = bounds[2];
+            origin[2] = bounds[4];
+            spacing[0] = 0;
+            spacing[1] = ySize/((double)(ydim-1));
+            spacing[2] = zSize/((double)(zdim-1));
+            _sliceAxis = X_AXIS;
+        } else if (minDir == 1) {
+            xdim = res;
+            ydim = 1;
+            zdim = res;
+            origin[0] = bounds[0];
+            origin[1] = bounds[2] + ySize/2.;
+            origin[2] = bounds[4];
+            spacing[0] = xSize/((double)(xdim-1));
+            spacing[1] = 0;
+            spacing[2] = zSize/((double)(zdim-1));
+            _sliceAxis = Y_AXIS;
+        } else {
+            xdim = res;
+            ydim = res;
+            zdim = 1;
+            origin[0] = bounds[0];
+            origin[1] = bounds[2];
+            origin[2] = bounds[4] + zSize/2.;
+            spacing[0] = xSize/((double)(xdim-1));
+            spacing[1] = ySize/((double)(ydim-1));
+            spacing[2] = 0;
+            _sliceAxis = Z_AXIS;
+        }
+        imageData->SetDimensions(xdim, ydim, zdim);
+        imageData->SetOrigin(origin);
+        imageData->SetSpacing(spacing);
+        _probeFilter->SetInputData(imageData);
+        _lic->SetInputConnection(_probeFilter->GetOutputPort());
+
+        if (_mapper == NULL) {
+            _mapper = vtkSmartPointer<vtkDataSetMapper>::New();
+            _mapper->SetColorModeToMapScalars();
+        }
+        _lic->Update();
+        if (1) {
+            vtkSmartPointer<vtkImageData> imgData = _probeFilter->GetImageDataOutput();
+            imgData->GetPointData()->SetActiveScalars(_probeFilter->GetValidPointMaskArrayName());
+            vtkSmartPointer<vtkImageCast> maskCast = vtkSmartPointer<vtkImageCast>::New();
+            maskCast->SetInputData(imgData);
+            //maskCast->SetInputConnection(_probeFilter->GetOutputPort());
+            //maskCast->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS,
+            //                                 _probeFilter->GetValidPointMaskArrayName());
+            maskCast->SetOutputScalarTypeToUnsignedChar();
+            vtkSmartPointer<vtkImageCast> licCast = vtkSmartPointer<vtkImageCast>::New();
+            licCast->SetInputConnection(_lic->GetOutputPort());
+            licCast->SetOutputScalarTypeToDouble();
+            vtkSmartPointer<vtkImageMask> mask = vtkSmartPointer<vtkImageMask>::New();
+            mask->SetInputConnection(0, licCast->GetOutputPort());
+            mask->SetInputConnection(1, maskCast->GetOutputPort());
+            _mapper->SetInputConnection(mask->GetOutputPort());
+        } else {
+            // Mask not working in VTK 6.1?
+            _mapper->SetInputConnection(_lic->GetOutputPort());
+        }
+    } else {
+        // DataSet is a 3D PolyData with cells
+        vtkPolyData *pd = vtkPolyData::SafeDownCast(ds);
+        assert(pd != NULL);
+
+        // XXX: This mapper seems to conflict with offscreen rendering, so the main
+        // renderer needs to be set not to use FBOs for this to work
+        _mapper = vtkSmartPointer<vtkPainterPolyDataMapper>::New();
+        vtkPainterPolyDataMapper *ppdmapper = vtkPainterPolyDataMapper::SafeDownCast(_mapper);
+        if (_painter == NULL) {
+            _painter = vtkSmartPointer<vtkSurfaceLICPainter>::New();
+            _painter->SetDelegatePainter(ppdmapper->GetPainter());
+         }
+        ppdmapper->SetPainter(_painter);
+        ppdmapper->SetInputData(pd);
+    }
+
+    setInterpolateBeforeMapping(true);
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getGrayDefault());
+    }
+
+    initProp();
+    getActor()->SetMapper(_mapper);
+
+    _mapper->Update();
+#endif
+}
+
+void LIC::setInterpolateBeforeMapping(bool state)
+{
+    if (_mapper != NULL) {
+        _mapper->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Select a 2D slice plane from a 3D DataSet
+ *
+ * \param[in] axis Axis of slice plane
+ * \param[in] ratio Position [0,1] of slice plane along axis
+ */
+void LIC::selectVolumeSlice(Axis axis, double ratio)
+{
+    if (_dataSet->is2D()) {
+        WARN("DataSet not 3D, returning");
+        return;
+    }
+
+    if (_volumeSlicer == NULL &&
+        _probeFilter == NULL) {
+        WARN("Called before update() or DataSet is not a volume");
+        return;
+    }
+
+    int dims[3];
+
+    vtkImageData *imageData = vtkImageData::SafeDownCast(_dataSet->getVtkDataSet());
+    if (imageData == NULL) {
+        if (_probeFilter != NULL) {
+            imageData = vtkImageData::SafeDownCast(_probeFilter->GetInput());
+            if (imageData == NULL) {
+                ERROR("Couldn't get probe filter input image");
+                return;
+            }
+        } else {
+            ERROR("Not a volume data set");
+            return;
+        }
+    }
+    imageData->GetDimensions(dims);
+
+    _sliceAxis = axis;
+
+    if (_probeFilter != NULL) {
+        vtkImageData *imageData = vtkImageData::SafeDownCast(_probeFilter->GetInput());
+        double bounds[6];
+        assert(vtkDataSet::SafeDownCast(_probeFilter->GetSource()) != NULL);
+        _dataSet->getBounds(bounds);
+        int dim = _resolution;
+
+        switch (axis) {
+        case X_AXIS:
+            imageData->SetDimensions(1, dim, dim);
+            imageData->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, bounds[2], bounds[4]);
+            imageData->SetSpacing(0,
+                                  (bounds[3]-bounds[2])/((double)(dim-1)), 
+                                  (bounds[5]-bounds[4])/((double)(dim-1)));
+            if (_cutPlane != NULL) {
+                _cutPlane->SetNormal(1, 0, 0);
+                _cutPlane->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
+            }
+            break;
+        case Y_AXIS:
+            imageData->SetDimensions(dim, 1, dim);
+            imageData->SetOrigin(bounds[0], bounds[2] + (bounds[3]-bounds[2])*ratio, bounds[4]);
+            imageData->SetSpacing((bounds[1]-bounds[0])/((double)(dim-1)), 
+                                  0,
+                                  (bounds[5]-bounds[4])/((double)(dim-1)));
+            if (_cutPlane != NULL) {
+                _cutPlane->SetNormal(0, 1, 0);
+                _cutPlane->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
+            }
+            break;
+        case Z_AXIS:
+            imageData->SetDimensions(dim, dim, 1);
+            imageData->SetOrigin(bounds[0], bounds[2], bounds[4] + (bounds[5]-bounds[4])*ratio);
+            imageData->SetSpacing((bounds[1]-bounds[0])/((double)(dim-1)), 
+                                  (bounds[3]-bounds[2])/((double)(dim-1)),
+                                  0);
+            if (_cutPlane != NULL) {
+                _cutPlane->SetNormal(0, 0, 1);
+                _cutPlane->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
+            }
+            break;
+        default:
+            ERROR("Invalid Axis");
+            return;
+        }
+    } else {
+        int voi[6];
+
+        switch (axis) {
+        case X_AXIS:
+            voi[0] = voi[1] = (int)((dims[0]-1) * ratio);
+            voi[2] = 0;
+            voi[3] = dims[1]-1;
+            voi[4] = 0;
+            voi[5] = dims[2]-1;
+            break;
+        case Y_AXIS:
+            voi[2] = voi[3] = (int)((dims[1]-1) * ratio);
+            voi[0] = 0;
+            voi[1] = dims[0]-1;
+            voi[4] = 0;
+            voi[5] = dims[2]-1;
+            break;
+        case Z_AXIS:
+            voi[4] = voi[5] = (int)((dims[2]-1) * ratio);
+            voi[0] = 0;
+            voi[1] = dims[0]-1;
+            voi[2] = 0;
+            voi[3] = dims[1]-1;
+            break;
+        default:
+            ERROR("Invalid Axis");
+            return;
+        }
+
+        _volumeSlicer->SetVOI(voi);
+    }
+#ifdef HAVE_LIC
+    if (_lic != NULL)
+        _lic->Update();
+#endif
+    if (_mapper != NULL)
+        _mapper->Update();
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void LIC::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void LIC::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_mapper != NULL) {
+            _mapper->UseLookupTableScalarRangeOff();
+            _mapper->SetLookupTable(_lut);
+        }
+    }
+
+    _lut->DeepCopy(cmap->getLookupTable());
+    _lut->SetRange(_dataRange);
+    _lut->Modified();
+}
+
+void LIC::updateRanges(Renderer *renderer)
+{
+    GraphicsObject::updateRanges(renderer);
+
+    if (_lut != NULL) {
+        _lut->SetRange(_dataRange);
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void LIC::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_mapper != NULL) {
+        _mapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/LIC.h
===================================================================
--- vtkvis/tags/1.8.4/LIC.h	(revision 6184)
+++ vtkvis/tags/1.8.4/LIC.h	(revision 6184)
@@ -0,0 +1,84 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_LIC_H
+#define VTKVIS_LIC_H
+
+#include <vtkSmartPointer.h>
+#include <vtkProp.h>
+#include <vtkPlaneCollection.h>
+#include <vtkExtractVOI.h>
+#include <vtkProbeFilter.h>
+#include <vtkImageDataLIC2D.h>
+#include <vtkSurfaceLICPainter.h>
+#include <vtkMapper.h>
+#include <vtkLookupTable.h>
+#include <vtkPlane.h>
+
+#include "ColorMap.h"
+#include "Types.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Line Integral Convolution visualization of vector fields
+ *
+ *  The DataSet must contain vectors
+ */
+class LIC : public GraphicsObject {
+public:
+    LIC();
+    virtual ~LIC();
+
+    virtual const char *getClassName() const
+    {
+        return "LIC";
+    }
+    
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void selectVolumeSlice(Axis axis, double ratio);
+
+    void setInterpolateBeforeMapping(bool state);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    virtual void initProp();
+    virtual void update();
+
+    Axis _sliceAxis;
+    ColorMap *_colorMap;
+    int _resolution;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkExtractVOI> _volumeSlicer;
+    vtkSmartPointer<vtkProbeFilter> _probeFilter;
+    vtkSmartPointer<vtkPlane> _cutPlane;
+#ifdef HAVE_LIC
+    vtkSmartPointer<vtkImageDataLIC2D> _lic;
+    vtkSmartPointer<vtkSurfaceLICPainter> _painter;
+#endif
+    vtkSmartPointer<vtkMapper> _mapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Line.cpp
===================================================================
--- vtkvis/tags/1.8.4/Line.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Line.cpp	(revision 6184)
@@ -0,0 +1,45 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkLineSource.h>
+
+#include "Line.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Line::Line() :
+    Shape()
+{
+}
+
+Line::~Line()
+{
+    TRACE("Deleting Line");
+}
+
+void Line::update()
+{
+    if (_line == NULL) {
+	_line = vtkSmartPointer<vtkLineSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    _pdMapper->SetInputConnection(_line->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
Index: vtkvis/tags/1.8.4/Line.h
===================================================================
--- vtkvis/tags/1.8.4/Line.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Line.h	(revision 6184)
@@ -0,0 +1,74 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_LINE_H
+#define VTKVIS_LINE_H
+
+#include <vector>
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkLineSource.h>
+#include <vtkPoints.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData Line
+ *
+ * This class creates a polyline
+ */
+class Line : public Shape
+{
+public:
+    Line();
+    virtual ~Line();
+
+    virtual const char *getClassName() const
+    {
+        return "Line";
+    }
+
+    /**
+     * \brief This interface creates a single line
+     */
+    void setEndPoints(double pt1[3], double pt2[3])
+    {
+        if (_line != NULL) {
+            _line->SetPoint1(pt1);
+            _line->SetPoint2(pt2);
+            _line->SetPoints(NULL);
+        }
+    }
+
+    /**
+     * \brief This interface creates a polyline
+     */
+    void setPoints(std::vector<double> pts)
+    {
+        if (_line == NULL)
+            return;
+        vtkPoints *points = vtkPoints::New();
+        for (size_t i = 0; i < pts.size(); i+=3) {
+            points->InsertNextPoint(pts[i], pts[i+1], pts[i+2]);
+        }
+        _line->SetPoints(points);
+    }
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkLineSource> _line;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Makefile.in
===================================================================
--- vtkvis/tags/1.8.4/Makefile.in	(revision 6184)
+++ vtkvis/tags/1.8.4/Makefile.in	(revision 6184)
@@ -0,0 +1,291 @@
+DEBUG			= #yes
+TRACE			= #yes
+USE_CUSTOM_AXES		= yes
+USE_FONT_CONFIG         = yes
+USE_GPU_RAYCASTING	= yes
+USE_OFFSCREEN_RENDERING	= #yes
+USE_OPENGL2_BACKEND     = #yes
+USE_THREADS		= yes
+USE_CPU_LEGEND_RENDER   = yes
+
+bindir          = @bindir@
+datadir         = @datadir@
+datarootdir     = @datarootdir@
+exec_prefix     = @exec_prefix@
+includedir      = @includedir@
+libdir          = @libdir@
+mandir          = @mandir@
+prefix          = @prefix@
+srcdir          = @srcdir@
+
+CC		= @CC@
+CXX		= @CXX@
+CFLAGS		= @CFLAGS@
+CXXFLAGS	= @CXXFLAGS@
+
+VPATH           = $(srcdir)
+
+INSTALL         = @INSTALL@
+INSTALL_PROGRAM = ${INSTALL} -m 0755
+INSTALL_DATA    = ${INSTALL} -m 0644
+INSTALL_SCRIPT  = ${INSTALL} -m 0644
+MKDIR_P		= @MKDIR_P@
+
+SVN_VERSION	= $(shell svnversion $(srcdir) | sed 's/Unversioned directory/unknown/')
+STATSDIR	= @STATSDIR@
+
+GL_LIB_SPEC	= -lGL -lm
+PTHREAD_LIB_SPEC= -lpthread
+
+TCL_LIB_SPEC    = @TCL_LIB_SPEC@
+TCL_INC_SPEC    = @TCL_INC_SPEC@
+
+VTK_VERSION     = @VTK_VERSION@
+VTK_LIB_DIR	= @VTK_LIB_DIR@
+VTK_INC_DIR     = @VTK_INC_DIR@
+VTK_INC_SPEC	= -I$(VTK_INC_DIR)/vtk-$(VTK_VERSION)
+VTK_LIB_SPEC    = -L$(VTK_LIB_DIR) \
+		-lvtkDomainsChemistry-$(VTK_VERSION) \
+		-lvtkIOCore-$(VTK_VERSION) \
+		-lvtkIOLegacy-$(VTK_VERSION) \
+		-lvtkFiltersExtraction-$(VTK_VERSION) \
+		-lvtkFiltersModeling-$(VTK_VERSION) \
+		-lvtkFiltersFlowPaths-$(VTK_VERSION) \
+		-lvtkFiltersGeometry-$(VTK_VERSION) \
+		-lvtkFiltersSources-$(VTK_VERSION) \
+		-lvtkFiltersGeneral-$(VTK_VERSION) \
+		-lvtkFiltersCore-$(VTK_VERSION) \
+		-lvtkImagingHybrid-$(VTK_VERSION) \
+		-lvtkImagingCore-$(VTK_VERSION) \
+		-lvtkInteractionStyle-$(VTK_VERSION) \
+		-lvtkInteractionWidgets-$(VTK_VERSION) \
+		-lvtkRenderingImage-$(VTK_VERSION) \
+		-lvtkRenderingFreeTypeFontConfig-$(VTK_VERSION) \
+		-lvtkRenderingFreeType-$(VTK_VERSION)  \
+		-lvtkRenderingVolume-$(VTK_VERSION) \
+		-lvtkRenderingLabel-$(VTK_VERSION) \
+		-lvtkRenderingAnnotation-$(VTK_VERSION) \
+		-lvtkRenderingCore-$(VTK_VERSION) \
+		-lvtkCommonCore-$(VTK_VERSION) \
+		-lvtkCommonDataModel-$(VTK_VERSION) \
+		-lvtkCommonExecutionModel-$(VTK_VERSION)  \
+		-lvtkCommonMisc-$(VTK_VERSION)  \
+		-lvtkCommonTransforms-$(VTK_VERSION) \
+		-lvtkCommonMath-$(VTK_VERSION) \
+		-lvtksys-$(VTK_VERSION)
+ifdef USE_OPENGL2_BACKEND
+VTK_BACKEND=OpenGL2
+else
+VTK_BACKEND=OpenGL
+endif
+
+ifeq ($(VTK_VERSION),6.3)
+VTK_LIB_SPEC += \
+		-lvtkRendering$(VTK_BACKEND)-$(VTK_VERSION) \
+		-lvtkRenderingVolume$(VTK_BACKEND)-$(VTK_VERSION)
+else
+VTK_LIB_SPEC += \
+		-lvtkRendering$(VTK_BACKEND)-$(VTK_VERSION) \
+		-lvtkRenderingFreeType$(VTK_BACKEND)-$(VTK_VERSION) \
+		-lvtkRenderingVolume$(VTK_BACKEND)-$(VTK_VERSION)
+endif
+
+ifeq ($(VTK_VERSION),6.0)
+VTK_LIB_SPEC += \
+		-lvtkRenderingHybridOpenGL-$(VTK_VERSION)
+else
+ifdef USE_OPENGL2_BACKEND
+VTK_LIB_SPEC += \
+		-lvtkRenderingLIC$(VTK_BACKEND)-$(VTK_VERSION)
+else
+VTK_LIB_SPEC += \
+		-lvtkRenderingLIC-$(VTK_VERSION)
+endif
+endif
+
+LD_RUN_PATH	= $(VTK_LIB_DIR):$(libdir)
+
+LIBS		= \
+		$(TCL_LIB_SPEC) \
+		$(VTK_LIB_SPEC) \
+		$(GL_LIB_SPEC) \
+		$(PTHREAD_LIB_SPEC) \
+		-Wl,-rpath,$(LD_RUN_PATH) \
+		-Wl,--enable-new-dtags
+
+INCLUDES	= \
+		-I$(srcdir) \
+		$(TCL_INC_SPEC) \
+		$(VTK_INC_SPEC)
+
+EXTRA_CXXFLAGS	= -Wall
+EXTRA_CFLAGS	= -Wall
+DEFINES		= -DSVN_VERSION=\"$(SVN_VERSION)\" -DSTATSDIR=\"$(STATSDIR)\"
+ifdef DEBUG
+DEFINES 	+= -DDEBUG
+CXXFLAGS	= -O0 -g
+endif
+ifdef TRACE
+DEFINES		+= -DWANT_TRACE
+endif
+ifdef USE_CUSTOM_AXES
+DEFINES 	+= -DUSE_CUSTOM_AXES
+endif
+ifdef USE_FONT_CONFIG
+DEFINES		+= -DUSE_FONT_CONFIG
+endif
+ifdef USE_OFFSCREEN_RENDERING
+DEFINES         += -DUSE_OFFSCREEN_RENDERING
+endif
+ifdef USE_GPU_RAYCASTING
+DEFINES         += -DUSE_GPU_RAYCAST_MAPPER
+endif
+ifdef USE_THREADS
+DEFINES         += -DUSE_THREADS
+endif
+ifdef USE_CPU_LEGEND_RENDER
+DEFINES         += -DLEGEND_SOFTWARE_RENDER
+endif
+ifndef USE_OPENGL2_BACKEND
+DEFINES         += -DHAVE_LIC
+endif
+
+ifeq ($(VTK_VERSION),6.3)
+VTK_MOD_DEFS    = -DvtkRenderingCore_AUTOINIT="3(vtkInteractionStyle,vtkRenderingFreeType,vtkRendering$(VTK_BACKEND))" -DvtkRenderingFreeType_AUTOINIT="1(vtkRenderingFreeTypeFontConfig)" -DvtkRenderingVolume_AUTOINIT="1(vtkRenderingVolume$(VTK_BACKEND))"
+else
+VTK_MOD_DEFS    = -DvtkRenderingCore_AUTOINIT="4(vtkInteractionStyle,vtkRenderingFreeType,vtkRenderingFreeType$(VTK_BACKEND),vtkRendering$(VTK_BACKEND))" -DvtkRenderingFreeType_AUTOINIT="1(vtkRenderingFreeTypeFontConfig)" -DvtkRenderingVolume_AUTOINIT="1(vtkRenderingVolume$(VTK_BACKEND))"
+endif
+DEFINES         += $(VTK_MOD_DEFS)
+
+CXX_SWITCHES	= $(CXXFLAGS) $(EXTRA_CXXFLAGS) $(DEFINES) $(INCLUDES)
+CC_SWITCHES	= $(CFLAGS) $(EXTRA_CFLAGS) $(DEFINES) $(INCLUDES)
+
+SERVER_SRCS	= \
+		Arc.cpp \
+		Arrow.cpp \
+		Box.cpp \
+		CmdProc.cpp \
+		ColorMap.cpp \
+		Cone.cpp \
+		Contour2D.cpp \
+		Contour3D.cpp \
+		Cutplane.cpp \
+		Cylinder.cpp \
+		DataSet.cpp \
+		Disk.cpp \
+		Glyphs.cpp \
+		GraphicsObject.cpp \
+		Group.cpp \
+		HeightMap.cpp \
+		Image.cpp \
+		ImageCutplane.cpp \
+		LIC.cpp \
+		Line.cpp \
+		Molecule.cpp \
+		Outline.cpp \
+		Parallelepiped.cpp \
+		PolyData.cpp \
+		Polygon.cpp \
+		PPMWriter.cpp \
+		PseudoColor.cpp \
+		ReadBuffer.cpp \
+		Renderer.cpp \
+		RendererCmd.cpp \
+		RendererGraphicsObjs.cpp \
+		RenderServer.cpp \
+		Shape.cpp \
+		Sphere.cpp \
+		Streamlines.cpp \
+		Text3D.cpp \
+		TGAWriter.cpp \
+		Trace.cpp \
+		Volume.cpp \
+		Warp.cpp
+
+ifdef USE_CUSTOM_AXES
+SERVER_SRCS+= \
+	vtkRpAxisActor.cpp \
+	vtkRpAxisFollower.cpp \
+	vtkRpCubeAxesActor.cpp
+endif
+ifdef USE_THREADS
+SERVER_SRCS+=ResponseQueue.cpp
+endif
+
+SERVER_OBJS=$(SERVER_SRCS:.cpp=.o)
+SERVER_OBJS+= md5.o
+SERVER=vtkvis
+
+.PHONY: all docs install clean clean-docs distclean
+
+all: $(SERVER)
+
+docs:
+	$(MKDIR_P) -m 0755 docs/doxygen
+	doxygen
+
+$(SERVER): $(SERVER_OBJS)
+	$(CXX) -o $@ $(SERVER_OBJS) $(LIBS)
+
+install: all
+	$(INSTALL_PROGRAM) $(SERVER) $(bindir)
+
+%.o: %.cpp
+	$(CXX) $(CXX_SWITCHES) -c $< -o $@
+
+%.o: %.c
+	$(CC) $(CC_SWITCHES) -c $< -o $@
+
+clean:
+	$(RM) *~ *.o $(SERVER)
+
+clean-docs:
+	$(RM) -r docs
+
+distclean: clean clean-docs
+	$(RM) Makefile Doxyfile
+
+Arc.o: Arc.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Arrow.o: Arrow.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Box.o: Box.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+CmdProc.o: CmdProc.h
+ColorMap.o: ColorMap.h Molecule.h Trace.h
+Cone.o: Cone.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Contour2D.o: Contour2D.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Contour3D.o: Contour3D.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+Cutplane.o: Cutplane.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+Cylinder.o: Cylinder.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+DataSet.o: DataSet.h Trace.h
+Disk.o: Disk.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Glyphs.o: Glyphs.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+GraphicsObject.o: GraphicsObject.h Renderer.h DataSet.h ColorMap.h Math.h Trace.h
+Group.o: Group.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+HeightMap.o: HeightMap.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+Image.o: Image.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+LIC.o: LIC.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h RenderServer.h
+Line.o: Line.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+md5.o: md5.h
+Molecule.o: Molecule.h MoleculeData.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+Outline.o: Outline.h GraphicsObject.h Math.h DataSet.h Trace.h
+Parallelepiped.o: Parallelepiped.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+PolyData.o: PolyData.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Polygon.o: Polygon.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+PPMWriter.o: PPMWriter.h ResponseQueue.h Trace.h
+PseudoColor.o: PseudoColor.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+ReadBuffer.o: ReadBuffer.h Trace.h
+Renderer.o: Renderer.h RendererGraphicsObjs.h vtkRpCubeAxesActor.h vtkRpAxisFollower.h vtkRpAxisActor.h Math.h DataSet.h Arc.h Arrow.h Box.h Cone.h Contour2D.h Contour3D.h Cutplane.h Cylinder.h Disk.h Glyphs.h Group.h HeightMap.h Image.h LIC.h Line.h Molecule.h Outline.h Parallelepiped.h PolyData.h Polygon.h PseudoColor.h Sphere.h Streamlines.h Text3D.h Volume.h Warp.h ColorMap.h Trace.h
+RendererCmd.o: Renderer.h RendererGraphicsObjs.h vtkRpCubeAxesActor.h vtkRpAxisFollower.h vtkRpAxisActor.h DataSet.h Arc.h Arrow.h Box.h Cone.h Contour2D.h Contour3D.h Cutplane.h Cylinder.h Disk.h Glyphs.h Group.h HeightMap.h Image.h LIC.h Line.h Molecule.h Outline.h Parallelepiped.h PolyData.h Polygon.h PseudoColor.h Sphere.h Streamlines.h Text3D.h Volume.h Warp.h ColorMap.h ReadBuffer.h ResponseQueue.h Trace.h CmdProc.h PPMWriter.h TGAWriter.h
+RendererGraphicsObjs.o: Renderer.h RendererGraphicsObjs.h DataSet.h Arc.h Arrow.h Box.h Cone.h Contour2D.h Contour3D.h Cutplane.h Cylinder.h Disk.h Glyphs.h Group.h HeightMap.h Image.h LIC.h Line.h Molecule.h Outline.h Parallelepiped.h PolyData.h Polygon.h PseudoColor.h Sphere.h Streamlines.h Text3D.h Volume.h Warp.h ColorMap.h Trace.h
+RenderServer.o: RenderServer.h RendererCmd.h Renderer.h vtkRpCubeAxesActor.h vtkRpAxisFollower.h vtkRpAxisActor.h ReadBuffer.h ResponseQueue.h Trace.h PPMWriter.h TGAWriter.h
+ResponseQueue.o: ResponseQueue.h Trace.h
+Shape.o: Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Sphere.o: Sphere.h Shape.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+Streamlines.o: Streamlines.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+Text3D.o: Text3D.h GraphicsObject.h Math.h DataSet.h Renderer.h Trace.h
+TGAWriter.o: TGAWriter.h ResponseQueue.h Trace.h
+Trace.o: Trace.h
+Volume.o: Volume.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
+vtkRpAxisActor.o: vtkRpAxisActor.h
+vtkRpAxisFollower.o: vtkRpAxisFollower.h vtkRpAxisActor.h
+vtkRpCubeAxesActor.o: vtkRpCubeAxesActor.h vtkRpAxisFollower.h vtkRpAxisActor.h
+Warp.o: Warp.h GraphicsObject.h Math.h DataSet.h Renderer.h ColorMap.h Trace.h
Index: vtkvis/tags/1.8.4/Math.h
===================================================================
--- vtkvis/tags/1.8.4/Math.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Math.h	(revision 6184)
@@ -0,0 +1,231 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_MATH_H
+#define VTKVIS_MATH_H
+
+#include <cmath>
+#include <cstring>
+
+#include <vtkMath.h>
+#include <vtkMatrix4x4.h>
+
+namespace VtkVis {
+
+/**
+ * \brief Convert a quaternion to an axis/angle rotation
+ * 
+ * \param[in] quat Quaternion with scalar first: wxyz
+ * \param[out] angleAxis axis/angle rotation with angle in degrees first
+ */
+inline void quatToAngleAxis(const double quat[4], double angleAxis[4])
+{
+    angleAxis[0] = vtkMath::DegreesFromRadians(2.0 * acos(quat[0]));
+    if (angleAxis[0] < 1.0e-6) {
+        angleAxis[1] = 1;
+        angleAxis[2] = 0;
+        angleAxis[3] = 0;
+    } else {
+        double denom = sqrt(1. - quat[0] * quat[0]);
+        angleAxis[1] = quat[1] / denom;
+        angleAxis[2] = quat[2] / denom;
+        angleAxis[3] = quat[3] / denom;
+    }
+}
+
+/**
+ * \brief Convert an axis/angle rotation to a quaternion
+ * 
+ * \param[in] angleAxis axis/angle rotation with angle in degrees first
+ * \param[out] quat Quaternion with scalar first: wxyz
+ */
+inline void angleAxisToQuat(const double angleAxis[4], double quat[4])
+{
+    quat[0] = cos(vtkMath::RadiansFromDegrees(angleAxis[0]) / 2.0);
+    double sinHalfAngle = sin(vtkMath::RadiansFromDegrees(angleAxis[0]) / 2.0);
+    quat[1] = angleAxis[1] * sinHalfAngle;
+    quat[2] = angleAxis[2] * sinHalfAngle;
+    quat[3] = angleAxis[3] * sinHalfAngle;
+}
+
+/**
+ * \brief Fill a vtkMatrix4x4 from a quaternion
+ *
+ * \param[in] quat Quaternion with scalar first: wxyz
+ * \param[out] mat Matrix to be filled in
+ */
+inline void quaternionToMatrix4x4(const double quat[4], vtkMatrix4x4& mat)
+{
+    double ww = quat[0]*quat[0];
+    double wx = quat[0]*quat[1];
+    double wy = quat[0]*quat[2];
+    double wz = quat[0]*quat[3];
+
+    double xx = quat[1]*quat[1];
+    double yy = quat[2]*quat[2];
+    double zz = quat[3]*quat[3];
+
+    double xy = quat[1]*quat[2];
+    double xz = quat[1]*quat[3];
+    double yz = quat[2]*quat[3];
+
+    double rr = xx + yy + zz;
+    // normalization factor, just in case quaternion was not normalized
+    double f = double(1)/double(sqrt(ww + rr));
+    double s = (ww - rr)*f;
+    f *= 2;
+
+    mat.SetElement(0, 0, xx*f + s);
+    mat.SetElement(1, 0, (xy + wz)*f);
+    mat.SetElement(2, 0, (xz - wy)*f);
+
+    mat.SetElement(0, 1, (xy - wz)*f);
+    mat.SetElement(1, 1, yy*f + s);
+    mat.SetElement(2, 1, (yz + wx)*f);
+
+    mat.SetElement(0, 2, (xz + wy)*f);
+    mat.SetElement(1, 2, (yz - wx)*f);
+    mat.SetElement(2, 2, zz*f + s);
+}
+
+/**
+ * \brief Fill a vtkMatrix4x4 from a quaternion, but with the matrix
+ * transposed.
+ *
+ * \param[in] quat Quaternion with scalar first: wxyz
+ * \param[out] mat Matrix to be filled in
+ */
+inline void quaternionToTransposeMatrix4x4(const double quat[4], vtkMatrix4x4& mat)
+{
+    double ww = quat[0]*quat[0];
+    double wx = quat[0]*quat[1];
+    double wy = quat[0]*quat[2];
+    double wz = quat[0]*quat[3];
+
+    double xx = quat[1]*quat[1];
+    double yy = quat[2]*quat[2];
+    double zz = quat[3]*quat[3];
+
+    double xy = quat[1]*quat[2];
+    double xz = quat[1]*quat[3];
+    double yz = quat[2]*quat[3];
+
+    double rr = xx + yy + zz;
+    // normalization factor, just in case quaternion was not normalized
+    double f = double(1)/double(sqrt(ww + rr));
+    double s = (ww - rr)*f;
+    f *= 2;
+
+    mat.SetElement(0, 0, xx*f + s);
+    mat.SetElement(0, 1, (xy + wz)*f);
+    mat.SetElement(0, 2, (xz - wy)*f);
+
+    mat.SetElement(1, 0, (xy - wz)*f);
+    mat.SetElement(1, 1, yy*f + s);
+    mat.SetElement(1, 2, (yz + wx)*f);
+
+    mat.SetElement(2, 0, (xz + wy)*f);
+    mat.SetElement(2, 1, (yz - wx)*f);
+    mat.SetElement(2, 2, zz*f + s);
+}
+
+/**
+ * \brief Multiply two quaternions and return the result
+ *
+ * Note: result can be the same memory location as one of the
+ * inputs
+ */
+inline double *quatMult(const double q1[4], const double q2[4], double result[4])
+{
+    double q1w = q1[0];
+    double q1x = q1[1];
+    double q1y = q1[2];
+    double q1z = q1[3];
+    double q2w = q2[0];
+    double q2x = q2[1];
+    double q2y = q2[2];
+    double q2z = q2[3];
+    result[0] = (q1w*q2w) - (q1x*q2x) - (q1y*q2y) - (q1z*q2z);
+    result[1] = (q1w*q2x) + (q1x*q2w) + (q1y*q2z) - (q1z*q2y);
+    result[2] = (q1w*q2y) + (q1y*q2w) + (q1z*q2x) - (q1x*q2z);
+    result[3] = (q1w*q2z) + (q1z*q2w) + (q1x*q2y) - (q1y*q2x);
+    return result;
+}
+
+/**
+ * \brief Compute the conjugate of a quaternion
+ *
+ * Note: result can be the same memory location as the input
+ */
+inline double *quatConjugate(const double quat[4], double result[4])
+{
+    if (result != quat)
+        result[0] = quat[0];
+    result[1] = -quat[1];
+    result[2] = -quat[2];
+    result[3] = -quat[3];
+    return result;
+}
+
+/**
+ * \brief Compute the reciprocal of a quaternion
+ *
+ * Note: result can be the same memory location as the input
+ */
+inline double *quatReciprocal(const double quat[4], double result[4])
+{
+    double denom = 
+        quat[0]*quat[0] + 
+        quat[1]*quat[1] + 
+        quat[2]*quat[2] + 
+        quat[3]*quat[3];
+    VtkVis::quatConjugate(quat, result);
+    for (int i = 0; i < 4; i++) {
+        result[i] /= denom;
+    }
+    return result;
+}
+
+/**
+ * \brief Compute the reciprocal of a quaternion in place
+ */
+inline double *quatReciprocal(double quat[4])
+{
+    return VtkVis::quatReciprocal(quat, quat);
+}
+
+/**
+ * \brief Deep copy a quaternion
+ */
+inline void copyQuat(const double quat[4], double result[4])
+{
+    memcpy(result, quat, sizeof(double)*4);
+}
+
+inline double min2(double a, double b)
+{
+    return ((a < b) ? a : b);
+}
+
+inline double max2(double a, double b)
+{
+    return ((a > b) ? a : b);
+}
+
+inline double min3(double a, double b, double c)
+{
+    return min2(min2(a, b), c);
+}
+
+inline double max3(double a, double b, double c)
+{
+    return max2(max2(a, b), c);
+}
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Molecule.cpp
===================================================================
--- vtkvis/tags/1.8.4/Molecule.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Molecule.cpp	(revision 6184)
@@ -0,0 +1,1114 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cstdio>
+#include <cfloat>
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkCellArray.h>
+#include <vtkPointData.h>
+#include <vtkFloatArray.h>
+#include <vtkDoubleArray.h>
+#include <vtkIntArray.h>
+#include <vtkStringArray.h>
+#include <vtkPolyData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkSphereSource.h>
+#include <vtkCylinderSource.h>
+#include <vtkTransform.h>
+#include <vtkTransformPolyDataFilter.h>
+#include <vtkGlyph3DMapper.h>
+#include <vtkPointSetToLabelHierarchy.h>
+#include <vtkLabelPlacementMapper.h>
+#include <vtkTextProperty.h>
+#include <vtkTransformPolyDataFilter.h>
+
+#include "Molecule.h"
+#include "MoleculeData.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Molecule::Molecule() :
+    GraphicsObject(),
+    _radiusScale(0.3),
+    _atomScaling(COVALENT_RADIUS),
+    _labelsOn(false),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_ELEMENTS),
+    _colorFieldType(DataSet::POINT_DATA)
+{
+    _bondColor[0] = _bondColor[1] = _bondColor[2] = 1.0f;
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+    _faceCulling = true;
+}
+
+Molecule::~Molecule()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Molecule for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Molecule with NULL DataSet");
+#endif
+}
+
+void Molecule::setDataSet(DataSet *dataSet,
+                          Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Create and initialize VTK Props to render a Molecule
+ */
+void Molecule::initProp()
+{
+    if (_atomProp == NULL) {
+        _atomProp = vtkSmartPointer<vtkActor>::New();
+        if (_faceCulling && _opacity == 1.0)
+            setCulling(_atomProp->GetProperty(), true);
+        _atomProp->GetProperty()->EdgeVisibilityOff();
+        _atomProp->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+        _atomProp->GetProperty()->SetLineWidth(_edgeWidth);
+        _atomProp->GetProperty()->SetOpacity(_opacity);
+        _atomProp->GetProperty()->SetAmbient(.2);
+        _atomProp->GetProperty()->SetSpecular(.2);
+        _atomProp->GetProperty()->SetSpecularPower(80.0);
+        if (!_lighting)
+            _atomProp->GetProperty()->LightingOff();
+    }
+    if (_bondProp == NULL) {
+        _bondProp = vtkSmartPointer<vtkActor>::New();
+        if (_faceCulling && _opacity == 1.0)
+            setCulling(_bondProp->GetProperty(), true);
+        _bondProp->GetProperty()->EdgeVisibilityOff();
+        _bondProp->GetProperty()->SetColor(_bondColor[0], _bondColor[1], _bondColor[2]);
+        _bondProp->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+        _bondProp->GetProperty()->SetLineWidth(_edgeWidth);
+        _bondProp->GetProperty()->SetOpacity(_opacity);
+        _bondProp->GetProperty()->SetAmbient(.2);
+        _bondProp->GetProperty()->SetSpecular(.2);
+        _bondProp->GetProperty()->SetSpecularPower(80.0);
+        if (!_lighting)
+            _bondProp->GetProperty()->LightingOff();
+    }
+    if (_labelProp == NULL) {
+        _labelProp = vtkSmartPointer<vtkActor2D>::New();
+    }
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkAssembly>::New();
+    }
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void Molecule::update()
+{
+    if (_dataSet == NULL) {
+        return;
+    }
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    if (_atomMapper == NULL) {
+        _atomMapper = vtkSmartPointer<vtkGlyph3DMapper>::New();
+        _atomMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _atomMapper->ScalarVisibilityOn();
+    }
+    if (_bondMapper == NULL) {
+        _bondMapper = vtkSmartPointer<vtkGlyph3DMapper>::New();
+        _bondMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _bondMapper->ScalarVisibilityOn();
+    }
+    if (_labelMapper == NULL) {
+        _labelMapper = vtkSmartPointer<vtkLabelPlacementMapper>::New();
+        _labelMapper->SetShapeToRoundedRect();
+        _labelMapper->SetBackgroundColor(1.0, 1.0, 0.7);
+        _labelMapper->SetBackgroundOpacity(0.8);
+        _labelMapper->SetMargin(3);
+    }
+
+    if (_lut == NULL) {
+        if (ds->GetPointData() == NULL ||
+            ds->GetPointData()->GetScalars() == NULL ||
+            strcmp(ds->GetPointData()->GetScalars()->GetName(), "element") != 0) {
+            WARN("No element array in dataset %s", _dataSet->getName().c_str());
+            setColorMap(ColorMap::getDefault());
+            if (_atomScaling != NO_ATOM_SCALING)
+                _atomScaling = NO_ATOM_SCALING;
+        } else {
+            TRACE("Using element default colormap");
+            setColorMap(ColorMap::getElementDefault());
+        }
+    }
+
+    initProp();
+
+    addLabelArray(ds);
+    addRadiusArray(ds, _atomScaling, _radiusScale);
+    computeBonds(ds);
+
+    vtkPolyData *pd = vtkPolyData::SafeDownCast(ds);
+    if (pd) {
+        TRACE("Points: %d Verts: %d Lines: %d Polys: %d Strips: %d",
+              pd->GetNumberOfPoints(),
+              pd->GetNumberOfVerts(),
+              pd->GetNumberOfLines(),
+              pd->GetNumberOfPolys(),
+              pd->GetNumberOfStrips());
+        // DataSet is a vtkPolyData
+        if (pd->GetNumberOfLines() > 0) {
+            // Bonds
+            setupBondPolyData();
+
+            if (_cylinderSource == NULL) {
+                _cylinderSource = vtkSmartPointer<vtkCylinderSource>::New();
+                _cylinderSource->SetRadius(0.075);
+                _cylinderSource->SetHeight(1.0);
+                _cylinderSource->SetResolution(12);
+                _cylinderSource->CappingOff();
+                _cylinderTrans = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+                _cylinderTrans->SetInputConnection(_cylinderSource->GetOutputPort());
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->RotateZ(-90.0);
+                _cylinderTrans->SetTransform(trans);
+            }
+            if (_lineSource == NULL) {
+                _lineSource = vtkSmartPointer<vtkLineSource>::New();
+                _lineSource->SetPoint1(-0.5, 0, 0);
+                _lineSource->SetPoint2(0.5, 0, 0);
+            }
+
+            _bondMapper->SetSourceConnection(_cylinderTrans->GetOutputPort());
+            _bondMapper->SetInputData(_bondPD);
+            _bondMapper->SetOrientationArray("bond_orientations");
+            _bondMapper->SetOrientationModeToDirection();
+            _bondMapper->OrientOn();
+            _bondMapper->SetScaleArray("bond_scales");
+            _bondMapper->SetScaleModeToScaleByVectorComponents();
+            _bondMapper->ScalingOn();
+            _bondMapper->ClampingOff();
+
+            _bondProp->SetMapper(_bondMapper);
+            getAssembly()->AddPart(_bondProp);
+        }
+        if (pd->GetNumberOfPoints() > 0) {
+            if (_labelHierarchy == NULL) {
+                _labelHierarchy = vtkSmartPointer<vtkPointSetToLabelHierarchy>::New();
+            }
+            if (_labelTransform == NULL) {
+                _labelTransform = vtkSmartPointer<vtkTransform>::New();
+            }
+            vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+            transformFilter->SetInputData(pd);
+            transformFilter->SetTransform(_labelTransform);
+            _labelHierarchy->SetInputConnection(transformFilter->GetOutputPort());
+            _labelHierarchy->SetLabelArrayName("_atom_labels");
+            _labelHierarchy->GetTextProperty()->SetColor(0, 0, 0);
+            _labelMapper->SetInputConnection(_labelHierarchy->GetOutputPort());
+            _labelProp->SetMapper(_labelMapper);
+
+            // Atoms
+            if (_sphereSource == NULL) {
+                _sphereSource = vtkSmartPointer<vtkSphereSource>::New();
+                _sphereSource->SetRadius(1.0);
+                _sphereSource->SetThetaResolution(14);
+                _sphereSource->SetPhiResolution(14);
+            }
+
+            _atomMapper->SetSourceConnection(_sphereSource->GetOutputPort());
+            _atomMapper->SetInputData(pd);
+            _atomMapper->SetScaleArray("_radii");
+            _atomMapper->SetScaleModeToScaleByMagnitude();
+            _atomMapper->ScalingOn();
+            _atomMapper->OrientOff();
+
+            _atomProp->SetMapper(_atomMapper);
+            getAssembly()->AddPart(_atomProp);
+        }
+    } else {
+        // DataSet is NOT a vtkPolyData
+        ERROR("DataSet is not a PolyData");
+        return;
+    }
+
+    setAtomLabelVisibility(_labelsOn);
+
+    if (pd->GetNumberOfPoints() > 0) {
+        _atomMapper->Update();
+        _labelMapper->Update();
+    }
+    if (pd->GetNumberOfLines() > 0) {
+        _bondMapper->Update();
+    }
+}
+
+void Molecule::updateLabelTransform()
+{
+    if (_labelTransform != NULL && getAssembly() != NULL) {
+        _labelTransform->SetMatrix(getAssembly()->GetMatrix());
+    }
+}
+
+void Molecule::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                         _dataSet->getActiveVectorsName(),
+                                         3);
+        for (int i = 0; i < 3; i++) {
+            renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                             _dataSet->getActiveVectorsName(),
+                                             3, i);
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+}
+
+void Molecule::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_ELEMENTS: // Assume default scalar is "element" array
+        setColorMode(mode,
+                     DataSet::POINT_DATA,
+                     "element");
+        break;
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void Molecule::setColorMode(ColorMode mode,
+                            const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void Molecule::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                            const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || (_atomMapper == NULL && _bondMapper == NULL))
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        if (_atomMapper != NULL)
+            _atomMapper->SetScalarModeToUsePointFieldData();
+        if (_bondMapper != NULL)
+            _bondMapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        if (_atomMapper != NULL)
+            _atomMapper->SetScalarModeToUseCellFieldData();
+        if (_bondMapper != NULL)
+            _bondMapper->SetScalarModeToUseCellFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (name != NULL && strlen(name) > 0) {
+        if (_atomMapper != NULL)
+            _atomMapper->SelectColorArray(name);
+        if (_bondMapper != NULL)
+            _bondMapper->SelectColorArray(name);
+    } else {
+        if (_atomMapper != NULL)
+            _atomMapper->SetScalarModeToDefault();
+        if (_bondMapper != NULL)
+            _bondMapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (mode == COLOR_BY_ELEMENTS) {
+            double range[2];
+            range[0] = 0;
+            range[1] = NUM_ELEMENTS;
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_ELEMENTS:
+        if (_atomMapper != NULL)
+            _atomMapper->ScalarVisibilityOn();
+        if (_bondMapper != NULL)
+            _bondMapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_SCALAR:
+        if (_atomMapper != NULL)
+            _atomMapper->ScalarVisibilityOn();
+        if (_bondMapper != NULL)
+            _bondMapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        if (_atomMapper != NULL)
+            _atomMapper->ScalarVisibilityOn();
+        if (_bondMapper != NULL)
+            _bondMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        if (_atomMapper != NULL)
+            _atomMapper->ScalarVisibilityOn();
+        if (_bondMapper != NULL)
+            _bondMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        if (_atomMapper != NULL)
+            _atomMapper->ScalarVisibilityOn();
+        if (_bondMapper != NULL)
+            _bondMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        if (_atomMapper != NULL)
+            _atomMapper->ScalarVisibilityOn();
+        if (_bondMapper != NULL)
+            _bondMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        if (_atomMapper != NULL)
+            _atomMapper->ScalarVisibilityOff();
+        if (_bondMapper != NULL)
+            _bondMapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void Molecule::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Molecule::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_atomMapper != NULL) {
+            _atomMapper->UseLookupTableScalarRangeOn();
+            _atomMapper->SetLookupTable(_lut);
+        }
+        if (_bondMapper != NULL) {
+            _bondMapper->UseLookupTableScalarRangeOn();
+            _bondMapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_BY_ELEMENTS: {
+            double range[2];
+            range[0] = 0;
+            range[1] = NUM_ELEMENTS;
+            _lut->SetRange(range);
+        }
+            break;
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+}
+
+void Molecule::setAtomLabelField(const char *fieldName)
+{
+    if (_labelHierarchy != NULL) {
+        if (strcmp(fieldName, "default") == 0) {
+            _labelHierarchy->SetLabelArrayName("_atom_labels");
+        } else {
+            _labelHierarchy->SetLabelArrayName(fieldName);
+        }
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of atom labels
+ */
+void Molecule::setAtomLabelVisibility(bool state)
+{
+    _labelsOn = state;
+    if (_labelProp != NULL) {
+        _labelProp->SetVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of the atoms
+ */
+void Molecule::setAtomVisibility(bool state)
+{
+    if (_atomProp != NULL) {
+        _atomProp->SetVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of the bonds
+ */
+void Molecule::setBondVisibility(bool state)
+{
+    if (_bondProp != NULL) {
+        _bondProp->SetVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Toggle visibility of the prop
+ */
+void Molecule::setVisibility(bool state)
+{
+    GraphicsObject::setVisibility(state);
+    if (_labelProp != NULL) {
+        if (!state)
+            _labelProp->SetVisibility(0);
+        else
+            setAtomLabelVisibility(_labelsOn);
+    }
+}
+
+/**
+ * \brief Set opacity of molecule
+ */
+void Molecule::setOpacity(double opacity)
+{
+    GraphicsObject::setOpacity(opacity);
+    if (_labelMapper != NULL) {
+        _labelMapper->SetBackgroundOpacity(opacity);
+    }
+}
+
+void Molecule::setAtomQuality(double quality)
+{
+    if (_sphereSource == NULL)
+        return;
+
+    if (quality > 10.0)
+        quality = 10.0;
+
+    int thetaRes = (int)(quality * 14.0);
+    int phiRes = (int)(quality * 14.0);
+    if (thetaRes < 4) thetaRes = 4;
+    if (phiRes < 3) phiRes = 3;
+
+    _sphereSource->SetThetaResolution(thetaRes);
+    _sphereSource->SetPhiResolution(phiRes);
+
+    if (_atomMapper != NULL) {
+        _atomMapper->Modified();
+        _atomMapper->Update();
+    }
+}
+
+void Molecule::setBondQuality(double quality)
+{
+    if (_cylinderSource == NULL)
+        return;
+
+    if (quality > 10.0)
+        quality = 10.0;
+
+    int res = (int)(quality * 12.0);
+    if (res < 3) res = 3;
+
+    _cylinderSource->SetResolution(res);
+
+    if (_bondMapper != NULL) {
+        _bondMapper->Modified();
+        _bondMapper->Update();
+    }
+}
+
+void Molecule::setBondStyle(BondStyle style)
+{
+    switch (style) {
+    case BOND_STYLE_CYLINDER:
+        if (_bondProp != NULL) {
+            _bondProp->GetProperty()->SetLineWidth(_edgeWidth);
+            _bondProp->GetProperty()->SetLighting(_lighting ? 1 : 0);
+        }
+        if (_bondMapper != NULL && _cylinderTrans != NULL &&
+            _bondMapper->GetInputConnection(1, 0) != _cylinderTrans->GetOutputPort()) {
+            _cylinderTrans->Modified();
+            _bondMapper->SetSourceConnection(_cylinderTrans->GetOutputPort());
+            _bondMapper->Modified();
+        }
+        break;
+    case BOND_STYLE_LINE:
+        if (_bondProp != NULL) {
+            _bondProp->GetProperty()->LightingOff();
+        }
+        if (_bondMapper != NULL && _lineSource != NULL &&
+            _bondMapper->GetInputConnection(1, 0) != _lineSource->GetOutputPort()) {
+            _lineSource->Modified();
+            _bondMapper->SetSourceConnection(_lineSource->GetOutputPort());
+            _bondMapper->Modified();
+        }
+         break;
+    default:
+        WARN("Unknown bond style");    
+    }
+}
+
+/**
+ * \brief Set constant bond color
+ */
+void Molecule::setBondColor(float color[3])
+{
+    _bondColor[0] = color[0];
+    _bondColor[1] = color[1];
+    _bondColor[2] = color[2];
+    if (_bondProp != NULL) {
+        _bondProp->GetProperty()->SetColor(_bondColor[0], _bondColor[1], _bondColor[2]);
+    }
+}
+
+/**
+ * \brief Set mode to determine how bonds are colored
+ */
+void Molecule::setBondColorMode(BondColorMode mode)
+{
+    if (_bondMapper == NULL) return;
+
+    switch (mode) {
+    case BOND_COLOR_BY_ELEMENTS:
+        _bondMapper->ScalarVisibilityOn();
+        break;
+    case BOND_COLOR_CONSTANT:
+        _bondMapper->ScalarVisibilityOff();
+        break;
+    default:
+        WARN("Unknown bond color mode");
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Molecule::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_atomMapper != NULL) {
+        _atomMapper->SetClippingPlanes(planes);
+    }
+    if (_bondMapper != NULL) {
+        _bondMapper->SetClippingPlanes(planes);
+    }
+}
+
+/**
+ * \brief Set the radius type used for scaling atoms
+ */
+void Molecule::setAtomScaling(AtomScaling state)
+{
+    if (state == _atomScaling)
+        return;
+
+    _atomScaling = state;
+    if (_dataSet != NULL) {
+        vtkDataSet *ds = _dataSet->getVtkDataSet();
+        addRadiusArray(ds, _atomScaling, _radiusScale);
+        if (_atomMapper != NULL) {
+             assert(ds->GetPointData() != NULL &&
+                    ds->GetPointData()->GetArray("_radii") != NULL);
+            _atomMapper->SetScaleModeToScaleByMagnitude();
+            _atomMapper->SetScaleArray("_radii");
+            _atomMapper->ScalingOn();
+        }
+    }
+}
+
+/**
+ * \brief Set the constant radius scaling factor for atoms.  This
+ * can be used to convert from Angstroms to atom coordinates units.
+ */
+void Molecule::setAtomRadiusScale(double scale)
+{
+    if (scale == _radiusScale)
+        return;
+
+    _radiusScale = scale;
+    if (_dataSet != NULL) {
+        vtkDataSet *ds = _dataSet->getVtkDataSet();
+        addRadiusArray(ds, _atomScaling, _radiusScale);
+    }
+}
+
+/**
+ * \brief Set the constant radius scaling factor for bonds.
+ */
+void Molecule::setBondRadiusScale(double scale)
+{
+    if (_cylinderSource != NULL &&
+        _cylinderSource->GetRadius() != scale) {
+        _cylinderSource->SetRadius(scale);
+        // Workaround bug with source modification not causing
+        // mapper to be updated
+        if (_bondMapper != NULL) {
+            _bondMapper->Modified();
+        }
+    }
+}
+
+void Molecule::setupBondPolyData()
+{
+    if (_dataSet == NULL)
+        return;
+    if (_bondPD == NULL) {
+        _bondPD = vtkSmartPointer<vtkPolyData>::New();
+    } else {
+        _bondPD->Initialize();
+    }
+    vtkPolyData *pd = vtkPolyData::SafeDownCast(_dataSet->getVtkDataSet());
+    if (pd == NULL)
+        return;
+    vtkCellArray *lines = pd->GetLines();
+    lines->InitTraversal();
+    vtkIdType npts, *pts;
+    vtkSmartPointer<vtkPoints> bondPoints = vtkSmartPointer<vtkPoints>::New();
+    vtkSmartPointer<vtkIntArray> bondElements = vtkSmartPointer<vtkIntArray>::New();
+    vtkSmartPointer<vtkDoubleArray> bondVectors = vtkSmartPointer<vtkDoubleArray>::New();
+    vtkSmartPointer<vtkDoubleArray> bondScales = vtkSmartPointer<vtkDoubleArray>::New();
+    bondElements->SetName("element");
+    bondVectors->SetName("bond_orientations");
+    bondVectors->SetNumberOfComponents(3);
+    bondScales->SetName("bond_scales");
+    bondScales->SetNumberOfComponents(3);
+    vtkDataArray *elements = NULL;
+    if (pd->GetPointData() != NULL &&
+        pd->GetPointData()->GetScalars() != NULL &&
+        strcmp(pd->GetPointData()->GetScalars()->GetName(), "element") == 0) {
+        elements = pd->GetPointData()->GetScalars();
+    }
+    for (int i = 0; lines->GetNextCell(npts, pts); i++) {
+        assert(npts == 2);
+        double pt0[3], pt1[3];
+        double newPt0[3], newPt1[3];
+        //double *pt0 = pd->GetPoint(pts[0]);
+        //double *pt1 = pd->GetPoint(pts[1]);
+        pd->GetPoint(pts[0], pt0);
+        pd->GetPoint(pts[1], pt1);
+        double center[3];
+
+        for (int j = 0; j < 3; j++)
+            center[j] = pt0[j] + (pt1[j] - pt0[j]) * 0.5;
+        for (int j = 0; j < 3; j++)
+            newPt0[j] = pt0[j] + (pt1[j] - pt0[j]) * 0.25;
+        for (int j = 0; j < 3; j++)
+            newPt1[j] = pt0[j] + (pt1[j] - pt0[j]) * 0.75;
+
+        bondPoints->InsertNextPoint(newPt0);
+        bondPoints->InsertNextPoint(newPt1);
+
+#ifdef DEBUG
+        TRACE("Bond %d: (%g,%g,%g)-(%g,%g,%g)-(%g,%g,%g)", i,
+              pt0[0], pt0[1], pt0[2], 
+              center[0], center[1], center[2],
+              pt1[0], pt1[1], pt1[2]);
+#endif
+
+        double vec[3];
+        for (int j = 0; j < 3; j++)
+            vec[j] = center[j] - pt0[j];
+
+        bondVectors->InsertNextTupleValue(vec);
+        bondVectors->InsertNextTupleValue(vec);
+#ifdef DEBUG
+        TRACE("Bond %d, vec: %g,%g,%g", i, vec[0], vec[1], vec[2]);
+#endif
+
+        double scale[3];
+        scale[0] = sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
+        scale[1] = 1.0;
+        scale[2] = 1.0;
+
+        bondScales->InsertNextTupleValue(scale);
+        bondScales->InsertNextTupleValue(scale);
+
+        if (elements != NULL) {
+            int element = (int)elements->GetComponent(pts[0], 0);
+#ifdef DEBUG
+            TRACE("Bond %d, elt 0: %d", i, element);
+#endif
+            bondElements->InsertNextValue(element);
+            element = (int)elements->GetComponent(pts[1], 0);
+#ifdef DEBUG
+            TRACE("Bond %d, elt 1: %d", i, element);
+#endif
+            bondElements->InsertNextValue(element);
+        }
+    }
+    _bondPD->SetPoints(bondPoints);
+    if (elements != NULL)
+        _bondPD->GetPointData()->SetScalars(bondElements);
+    _bondPD->GetPointData()->AddArray(bondVectors);
+    _bondPD->GetPointData()->AddArray(bondScales);
+}
+
+void Molecule::addLabelArray(vtkDataSet *dataSet)
+{
+    vtkDataArray *elements = NULL;
+    if (dataSet->GetPointData() != NULL &&
+        dataSet->GetPointData()->GetScalars() != NULL &&
+        strcmp(dataSet->GetPointData()->GetScalars()->GetName(), "element") == 0) {
+        elements = dataSet->GetPointData()->GetScalars();
+    } else {
+        WARN("Can't label atoms without an element array");
+    }
+
+    vtkSmartPointer<vtkStringArray> labelArray = vtkSmartPointer<vtkStringArray>::New();
+    labelArray->SetName("_atom_labels");
+    vtkPolyData *pd = vtkPolyData::SafeDownCast(dataSet);
+    if (pd == NULL) {
+        ERROR("DataSet not a PolyData");
+        return;
+    }
+    for (int i = 0; i < pd->GetNumberOfPoints(); i++) {
+        char buf[32];
+        if (elements != NULL) {
+            int elt = (int)elements->GetComponent(i, 0);
+            sprintf(buf, "%s%d", g_elementNames[elt], i);
+        } else {
+            sprintf(buf, "%d", i);
+        }
+        labelArray->InsertNextValue(buf);
+    }
+    dataSet->GetPointData()->AddArray(labelArray);
+}
+
+/**
+ * \brief Add a scalar array to dataSet with sizes for the elements
+ * specified in the "element" scalar array
+ */
+void Molecule::addRadiusArray(vtkDataSet *dataSet, AtomScaling scaling, double scaleFactor)
+{
+    vtkDataArray *elements = NULL;
+    if (dataSet->GetPointData() != NULL &&
+        dataSet->GetPointData()->GetScalars() != NULL &&
+        strcmp(dataSet->GetPointData()->GetScalars()->GetName(), "element") == 0) {
+        elements = dataSet->GetPointData()->GetScalars();
+    } else if (scaling != NO_ATOM_SCALING) {
+        WARN("Can't use non-constant scaling without an element array");
+    }
+    const float *radiusSource = NULL;
+    switch (scaling) {
+    case VAN_DER_WAALS_RADIUS:
+        radiusSource = g_vdwRadii;
+        break;
+    case COVALENT_RADIUS:
+        radiusSource = g_covalentRadii;
+        break;
+    case ATOMIC_RADIUS:
+        radiusSource = g_atomicRadii;
+        break;
+    case NO_ATOM_SCALING:
+    default:
+        ;
+    }
+    vtkPolyData *pd = vtkPolyData::SafeDownCast(dataSet);
+    if (pd == NULL) {
+        ERROR("DataSet not a PolyData");
+        return;
+    }
+    vtkDataArray *array = dataSet->GetPointData()->GetArray("_radii");
+    vtkSmartPointer<vtkFloatArray> radii;
+    if (array == NULL) {
+        radii = vtkSmartPointer<vtkFloatArray>::New();
+        radii->SetName("_radii");
+        radii->SetNumberOfComponents(1);
+        radii->SetNumberOfValues(pd->GetNumberOfPoints());
+    } else {
+        radii = vtkFloatArray::SafeDownCast(array);
+        assert(radii != NULL);
+    }
+    for (int i = 0; i < pd->GetNumberOfPoints(); i++) {
+        float value;
+        if (elements != NULL && radiusSource != NULL) {
+            int elt = (int)elements->GetComponent(i, 0);
+            value = radiusSource[elt] * scaleFactor;
+        } else {
+            value = scaleFactor;
+        }
+        radii->SetValue(i, value);
+    }
+    radii->Modified();
+    if (array == NULL) {
+        dataSet->GetPointData()->AddArray(radii);
+    }
+}
+
+/**
+ * \brief Create a color map to map atomic numbers to element colors
+ */
+ColorMap *Molecule::createElementColorMap()
+{
+    ColorMap *elementCmap = new ColorMap("elementDefault");
+    ColorMap::ControlPoint cp[NUM_ELEMENTS+1];
+
+    elementCmap->setNumberOfTableEntries(NUM_ELEMENTS+1);
+    for (int i = 0; i <= NUM_ELEMENTS; i++) {
+        cp[i].value = i/((double)NUM_ELEMENTS);
+        for (int c = 0; c < 3; c++) {
+            cp[i].color[c] = ((double)g_elementColors[i][c])/255.;
+        }
+        elementCmap->addControlPoint(cp[i]);
+    }
+    ColorMap::OpacityControlPoint ocp[2];
+    ocp[0].value = 0;
+    ocp[0].alpha = 1.0;
+    ocp[1].value = 1.0;
+    ocp[1].alpha = 1.0;
+    elementCmap->addOpacityControlPoint(ocp[0]);
+    elementCmap->addOpacityControlPoint(ocp[1]);
+    elementCmap->build();
+    double range[2];
+    range[0] = 0;
+    range[1] = NUM_ELEMENTS;
+    elementCmap->getLookupTable()->SetRange(range);
+
+    return elementCmap;
+}
+
+int Molecule::computeBonds(vtkDataSet *dataSet, float tolerance)
+{
+    vtkPolyData *pd = vtkPolyData::SafeDownCast(dataSet);
+    if (pd == NULL) {
+        ERROR("DataSet not a PolyData");
+        return -1;
+    }
+
+    if (pd->GetNumberOfPoints() < 2 ||
+        pd->GetNumberOfLines() > 0) {
+        return 0;
+    }
+
+    vtkDataArray *elements = NULL;
+    if (pd->GetPointData() != NULL &&
+        pd->GetPointData()->GetScalars() != NULL &&
+        strcmp(pd->GetPointData()->GetScalars()->GetName(), "element") == 0) {
+        elements = pd->GetPointData()->GetScalars();
+    } else {
+        TRACE("Can't compute bonds without an element array");
+        return -1;
+    }
+    int numAtoms = pd->GetNumberOfPoints();
+    vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
+    for (int i = 0; i < numAtoms; i++) {
+        double pt1[3];
+        int elem1 = (int)elements->GetComponent(i, 0);
+        float r1 = g_covalentRadii[elem1];
+        pd->GetPoint(i, pt1);
+        for (int j = i+1; j < numAtoms; j++) {
+            float r2, dist;
+            double pt2[3];
+            int elem2 = (int)elements->GetComponent(j, 0);
+
+            if (elem1 == 1 && elem2 == 1)
+                continue;
+
+            r2 = g_covalentRadii[elem2];
+            pd->GetPoint(j, pt2);
+
+            double x = pt1[0] - pt2[0];
+            double y = pt1[1] - pt2[1];
+            double z = pt1[2] - pt2[2];
+            dist = (float)sqrt(x*x + y*y + z*z);
+            //TRACE("i=%d,j=%d elem1=%d,elem2=%d r1=%g,r2=%g, dist=%g", i, j, elem1, elem2, r1, r2, dist); 
+            if (dist < (r1 + r2 + tolerance)) {
+                vtkIdType pts[2];
+                pts[0] = i;
+                pts[1] = j;
+                cells->InsertNextCell(2, pts);
+            }
+        }
+    }
+
+    if (cells->GetNumberOfCells() > 0) {
+        pd->SetLines(cells);
+    }
+
+    TRACE("Generated %d bonds", cells->GetNumberOfCells());
+    return cells->GetNumberOfCells();
+}
Index: vtkvis/tags/1.8.4/Molecule.h
===================================================================
--- vtkvis/tags/1.8.4/Molecule.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Molecule.h	(revision 6184)
@@ -0,0 +1,225 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_MOLECULE_H
+#define VTKVIS_MOLECULE_H
+
+#include <vtkSmartPointer.h>
+#include <vtkLookupTable.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkActor2D.h>
+#include <vtkAssembly.h>
+#include <vtkGlyph3DMapper.h>
+#include <vtkTransformPolyDataFilter.h>
+#include <vtkSphereSource.h>
+#include <vtkCylinderSource.h>
+#include <vtkLineSource.h>
+#include <vtkPointSetToLabelHierarchy.h>
+#include <vtkLabelPlacementMapper.h>
+#include <vtkTransform.h>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Ball and stick Molecule
+ *
+ * The data format for this class is based on the VisIt Molecule
+ * plot.  It consists of a vtkPolyData with atom coordinates as
+ * points and vertices and bonds as lines.  A scalar array named
+ * "element" can be included as point data to specify atomic 
+ * numbers to use in coloring (via the "elementDefault" ColorMap)
+ * and scaling atoms.  If the scalar array is not named 
+ * "element," it will be color-mapped as a standard scalar field.
+ */
+class Molecule : public GraphicsObject {
+public:
+    enum AtomScaling {
+        NO_ATOM_SCALING,
+        VAN_DER_WAALS_RADIUS,
+        COVALENT_RADIUS,
+        ATOMIC_RADIUS
+    };
+    enum BondStyle {
+        BOND_STYLE_CYLINDER,
+        BOND_STYLE_LINE
+    };
+    enum ColorMode {
+        COLOR_BY_ELEMENTS,
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+    enum BondColorMode {
+        BOND_COLOR_BY_ELEMENTS,
+        BOND_COLOR_CONSTANT
+    };
+
+    Molecule();
+    virtual ~Molecule();
+
+    virtual const char *getClassName() const
+    {
+        return "Molecule";
+    }
+
+    virtual vtkProp *getOverlayProp()
+    {
+        return _labelProp;
+    }
+
+    virtual void setTransform(vtkMatrix4x4 *matrix)
+    {
+        GraphicsObject::setTransform(matrix);
+        updateLabelTransform();
+    }
+
+    virtual void setOrigin(double origin[3])
+    {
+        GraphicsObject::setOrigin(origin);
+        updateLabelTransform();
+    }
+
+    virtual void setOrientation(double quat[4])
+    {
+        GraphicsObject::setOrientation(quat);
+        updateLabelTransform();
+    }
+
+    virtual void setOrientation(double angle, double axis[3])
+    {
+        GraphicsObject::setOrientation(angle, axis);
+        updateLabelTransform();
+    }
+
+    virtual void setPosition(double pos[3])
+    {
+        GraphicsObject::setPosition(pos);
+        updateLabelTransform();
+    }
+
+    virtual void setAspect(double aspect)
+    {
+        GraphicsObject::setAspect(aspect);
+        updateLabelTransform();
+    }
+
+    virtual void setScale(double scale[3])
+    {
+        GraphicsObject::setScale(scale);
+        updateLabelTransform();
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+    void setAtomScaling(AtomScaling state);
+
+    void setAtomRadiusScale(double scale);
+
+    void setBondRadiusScale(double scale);
+
+    virtual void setVisibility(bool state);
+
+    virtual void setOpacity(double opacity);
+
+    void setAtomVisibility(bool state);
+
+    void setAtomLabelVisibility(bool state);
+
+    void setAtomLabelField(const char *fieldName);
+
+    void setBondVisibility(bool state);
+
+    void setBondStyle(BondStyle style);
+
+    void setBondColor(float color[3]);
+
+    void setBondColorMode(BondColorMode mode);
+
+    void setAtomQuality(double quality);
+
+    void setBondQuality(double quality);
+
+    static ColorMap *createElementColorMap();
+
+private:
+    virtual void initProp();
+    virtual void update();
+
+    void setupBondPolyData();
+
+    void updateLabelTransform();
+
+    static void addLabelArray(vtkDataSet *dataSet);
+
+    static void addRadiusArray(vtkDataSet *dataSet, AtomScaling scaling, double scaleFactor);
+
+    static int computeBonds(vtkDataSet *dataSet, float tolerance = 0.45f);
+
+    float _bondColor[3];
+    double _radiusScale;
+    AtomScaling _atomScaling;
+    bool _labelsOn;
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkActor> _atomProp;
+    vtkSmartPointer<vtkActor> _bondProp;
+    vtkSmartPointer<vtkActor2D> _labelProp;
+    vtkSmartPointer<vtkSphereSource> _sphereSource;
+    vtkSmartPointer<vtkPolyData> _bondPD;
+    vtkSmartPointer<vtkCylinderSource> _cylinderSource;
+    vtkSmartPointer<vtkTransformPolyDataFilter>_cylinderTrans;
+    vtkSmartPointer<vtkLineSource> _lineSource;
+    vtkSmartPointer<vtkGlyph3DMapper> _atomMapper;
+    vtkSmartPointer<vtkGlyph3DMapper> _bondMapper;
+    vtkSmartPointer<vtkPointSetToLabelHierarchy> _labelHierarchy;
+    vtkSmartPointer<vtkLabelPlacementMapper> _labelMapper;
+    vtkSmartPointer<vtkTransform> _labelTransform;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/MoleculeData.h
===================================================================
--- vtkvis/tags/1.8.4/MoleculeData.h	(revision 6184)
+++ vtkvis/tags/1.8.4/MoleculeData.h	(revision 6184)
@@ -0,0 +1,578 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#define NUM_ELEMENTS 109
+
+// jmol colors (taken from VisIt)
+const unsigned char g_elementColors[NUM_ELEMENTS+1][3] = {
+    { 0x20, 0x20, 0x20 }, // ?
+    { 0xFF, 0xFF, 0xFF }, // H
+    { 0xD9, 0xFF, 0xFF }, // He
+    { 0xCC, 0x80, 0xFF }, // Li
+    { 0xC2, 0xFF, 0x00 }, // Be
+    { 0xFF, 0xB5, 0xB5 }, // B
+    { 0x90, 0x90, 0x90 }, // C
+    { 0x30, 0x50, 0xF8 }, // N
+    { 0xFF, 0x0D, 0x0D }, // O
+    { 0x90, 0xE0, 0x50 }, // F
+    { 0xB3, 0xE3, 0xF5 }, // Ne
+    { 0xAB, 0x5C, 0xF2 }, // Na
+    { 0x8A, 0xFF, 0x00 }, // Mg
+    { 0xBF, 0xA6, 0xA6 }, // Al
+    { 0xF0, 0xC8, 0xA0 }, // Si
+    { 0xFF, 0x80, 0x00 }, // P
+    { 0xFF, 0xFF, 0x30 }, // S
+    { 0x1F, 0xF0, 0x1F }, // Cl
+    { 0x80, 0xD1, 0xE3 }, // Ar
+    { 0x8F, 0x40, 0xD4 }, // K
+    { 0x3D, 0xFF, 0x00 }, // Ca
+    { 0xE6, 0xE6, 0xE6 }, // Sc
+    { 0xBF, 0xC2, 0xC7 }, // Ti
+    { 0xA6, 0xA6, 0xAB }, // V
+    { 0x8A, 0x99, 0xC7 }, // Cr
+    { 0x9C, 0x7A, 0xC7 }, // Mn
+    { 0xE0, 0x66, 0x33 }, // Fe
+    { 0xF0, 0x90, 0xA0 }, // Co
+    { 0x50, 0xD0, 0x50 }, // Ni
+    { 0xC8, 0x80, 0x33 }, // Cu
+    { 0x7D, 0x80, 0xB0 }, // Zn
+    { 0xC2, 0x8F, 0x8F }, // Ga
+    { 0x66, 0x8F, 0x8F }, // Ge
+    { 0xBD, 0x80, 0xE3 }, // As
+    { 0xFF, 0xA1, 0x00 }, // Se
+    { 0xA6, 0x29, 0x29 }, // Br
+    { 0x5C, 0xB8, 0xD1 }, // Kr
+    { 0x70, 0x2E, 0xB0 }, // Rb
+    { 0x00, 0xFF, 0x00 }, // Sr
+    { 0x94, 0xFF, 0xFF }, // Y
+    { 0x94, 0xE0, 0xE0 }, // Zr
+    { 0x73, 0xC2, 0xC9 }, // Nb
+    { 0x54, 0xB5, 0xB5 }, // Mo
+    { 0x3B, 0x9E, 0x9E }, // Tc
+    { 0x24, 0x8F, 0x8F }, // Ru
+    { 0x0A, 0x7D, 0x8C }, // Rh
+    { 0x00, 0x69, 0x85 }, // Pd
+    { 0xC0, 0xC0, 0xC0 }, // Ag
+    { 0xFF, 0xD9, 0x8F }, // Cd
+    { 0xA6, 0x75, 0x73 }, // In
+    { 0x66, 0x80, 0x80 }, // Sn
+    { 0x9E, 0x63, 0xB5 }, // Sb
+    { 0xD4, 0x7A, 0x00 }, // Te
+    { 0x94, 0x00, 0x94 }, // I
+    { 0x42, 0x9E, 0xB0 }, // Xe
+    { 0x57, 0x17, 0x8F }, // Cs
+    { 0x00, 0xC9, 0x00 }, // Ba
+    { 0x70, 0xD4, 0xFF }, // La
+    { 0xFF, 0xFF, 0xC7 }, // Ce
+    { 0xD9, 0xFF, 0xC7 }, // Pr
+    { 0xC7, 0xFF, 0xC7 }, // Nd
+    { 0xA3, 0xFF, 0xC7 }, // Pm
+    { 0x8F, 0xFF, 0xC7 }, // Sm
+    { 0x61, 0xFF, 0xC7 }, // Eu
+    { 0x45, 0xFF, 0xC7 }, // Gd
+    { 0x30, 0xFF, 0xC7 }, // Tb
+    { 0x1F, 0xFF, 0xC7 }, // Dy
+    { 0x00, 0xFF, 0x9C }, // Ho
+    { 0x00, 0xE6, 0x75 }, // Er
+    { 0x00, 0xD4, 0x52 }, // Tm
+    { 0x00, 0xBF, 0x38 }, // Yb
+    { 0x00, 0xAB, 0x24 }, // Lu
+    { 0x4D, 0xC2, 0xFF }, // Hf
+    { 0x4D, 0xA6, 0xFF }, // Ta
+    { 0x21, 0x94, 0xD6 }, // W
+    { 0x26, 0x7D, 0xAB }, // Re
+    { 0x26, 0x66, 0x96 }, // Os
+    { 0x17, 0x54, 0x87 }, // Ir
+    { 0xD0, 0xD0, 0xE0 }, // Pt
+    { 0xFF, 0xD1, 0x23 }, // Au
+    { 0xB8, 0xB8, 0xD0 }, // Hg
+    { 0xA6, 0x54, 0x4D }, // Tl
+    { 0x57, 0x59, 0x61 }, // Pb
+    { 0x9E, 0x4F, 0xB5 }, // Bi
+    { 0xAB, 0x5C, 0x00 }, // Po
+    { 0x75, 0x4F, 0x45 }, // At
+    { 0x42, 0x82, 0x96 }, // Rn
+    { 0x42, 0x00, 0x66 }, // Fr
+    { 0x00, 0x7D, 0x00 }, // Ra
+    { 0x70, 0xAB, 0xFA }, // Ac
+    { 0x00, 0xBA, 0xFF }, // Th
+    { 0x00, 0xA1, 0xFF }, // Pa
+    { 0x00, 0x8F, 0xFF }, // U
+    { 0x00, 0x80, 0xFF }, // Np
+    { 0x00, 0x6B, 0xFF }, // Pu
+    { 0x54, 0x5C, 0xF2 }, // Am
+    { 0x78, 0x5C, 0xE3 }, // Cm
+    { 0x8A, 0x4F, 0xE3 }, // Bk
+    { 0xA1, 0x36, 0xD4 }, // Cf
+    { 0xB3, 0x1F, 0xD4 }, // Es
+    { 0xB3, 0x1F, 0xBA }, // Fm
+    { 0xB3, 0x0D, 0xA6 }, // Md
+    { 0xBD, 0x0D, 0x87 }, // No
+    { 0xC7, 0x00, 0x66 }, // Lr
+    { 0xCC, 0x00, 0x59 }, // Rf
+    { 0xD1, 0x00, 0x4F }, // Db
+    { 0xD9, 0x00, 0x45 }, // Sg
+    { 0xE0, 0x00, 0x38 }, // Bh
+    { 0xE6, 0x00, 0x2E }, // Hs
+    { 0xEB, 0x00, 0x26 }  // Mt
+};
+
+// Covalent radii in Angstroms from VisIt
+const float g_covalentRadii[NUM_ELEMENTS+1] = {
+     .1f,  // ?  0
+    0.32f, // H  1
+    0.93f, // He 2
+    1.23f, // Li 3
+    0.90f, // Be 4
+    0.82f, // B  5
+    0.77f, // C  6
+    0.75f, // N  7
+    0.73f, // O  8
+    0.72f, // F  9
+    0.71f, // Ne 10
+    1.54f, // Na 11
+    1.36f, // Mg 12
+    1.18f, // Al 13
+    1.11f, // Si 14
+    1.06f, // P  15
+    1.02f, // S  16
+    0.99f, // Cl 17
+    0.98f, // Ar 18
+    2.03f, // K  19
+    1.74f, // Ca 20
+    1.44f, // Sc 21
+    1.32f, // Ti 22
+    1.22f, // V  23
+    1.18f, // Cr 24
+    1.17f, // Mn 25
+    1.17f, // Fe 26
+    1.16f, // Co 27
+    1.15f, // Ni 28
+    1.17f, // Cu 29
+    1.25f, // Zn 30
+    1.26f, // Ga 31
+    1.22f, // Ge 32
+    1.20f, // As 33
+    1.16f, // Se 34
+    1.14f, // Br 35
+    1.12f, // Kr 36
+    2.16f, // Rb 37
+    1.91f, // Sr 38
+    1.62f, // Y  39
+    1.45f, // Zr 40
+    1.34f, // Nb 41
+    1.30f, // Mo 42
+    1.27f, // Tc 43
+    1.25f, // Ru 44
+    1.25f, // Rh 45
+    1.28f, // Pd 46
+    1.34f, // Ag 47
+    1.48f, // Cd 48
+    1.44f, // In 49
+    1.41f, // Sn 50
+    1.40f, // Sb 51
+    1.36f, // Te 52
+    1.33f, // I  53
+    1.31f, // Xe 54
+    2.35f, // Cs 55
+    1.98f, // Ba 56
+    1.69f, // La 57
+    1.65f, // Ce 58
+    1.65f, // Pr 59
+    1.64f, // Nd 60
+    1.63f, // Pm 61
+    1.62f, // Sm 62
+    1.85f, // Eu 63
+    1.61f, // Gd 64
+    1.59f, // Tb 65
+    1.59f, // Dy 66
+    1.58f, // Ho 67
+    1.57f, // Er 68
+    1.56f, // Tm 69
+    1.74f, // Yb 70
+    1.56f, // Lu 71
+    1.44f, // Hf 72
+    1.34f, // Ta 73
+    1.30f, // W  74
+    1.28f, // Re 75
+    1.26f, // Os 76
+    1.27f, // Ir 77
+    1.30f, // Pt 78
+    1.34f, // Au 79
+    1.49f, // Hg 80
+    1.48f, // Tl 81
+    1.47f, // Pb 82
+    1.46f, // Bi 83
+    1.46f, // Po 84
+    1.45f, // At 85
+    1.50f, // Rn 86 // 86-96 from en.wikipedia.org/wiki/Covalent_radius 10/6/09
+    2.60f, // Fr 87
+    2.21f, // Ra 88
+    2.15f, // Ac 89
+    2.06f, // Th 90
+    2.00f, // Pa 91
+    1.96f, // U  92
+    1.90f, // Np 93
+    1.87f, // Pu 94
+    1.80f, // Am 95
+    1.69f, // Cm 96
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f
+};
+
+// Atomic radii in Angstroms from VisIt
+const float g_atomicRadii[NUM_ELEMENTS+1] = {
+     .1f,  // ?  0
+    0.79f, // H  1
+    0.49f, // He 2
+    2.05f, // Li 3
+    1.40f, // Be 4
+    1.17f, // B  5
+    0.91f, // C  6
+    0.75f, // N  7
+    0.65f, // O  8
+    0.57f, // F  9
+    0.51f, // Ne 10
+    2.23f, // Na 11
+    1.72f, // Mg 12
+    1.82f, // Al 13
+    1.46f, // Si 14
+    1.23f, // P  15
+    1.09f, // S  16
+    0.97f, // Cl 17
+    0.88f, // Ar 18
+    2.77f, // K  19
+    2.23f, // Ca 20
+    2.09f, // Sc 21
+    2.00f, // Ti 22
+    1.92f, // V  23
+    1.85f, // Cr 24
+    1.79f, // Mn 25
+    1.72f, // Fe 26
+    1.67f, // Co 27
+    1.62f, // Ni 28
+    1.57f, // Cu 29
+    1.53f, // Zn 30
+    1.81f, // Ga 31
+    1.52f, // Ge 32
+    1.33f, // As 33
+    1.22f, // Se 34
+    1.12f, // Br 35
+    1.03f, // Kr 36
+    2.98f, // Rb 37
+    2.45f, // Sr 38
+    2.27f, // Y  39
+    2.16f, // Zr 40
+    2.08f, // Nb 41
+    2.01f, // Mo 42
+    1.95f, // Tc 43
+    1.89f, // Ru 44
+    1.83f, // Rh 45
+    1.79f, // Pd 46
+    1.75f, // Ag 47
+    1.71f, // Cd 48
+    2.00f, // In 49
+    1.72f, // Sn 50
+    1.53f, // Sb 51
+    1.42f, // Te 52
+    1.32f, // I  53
+    1.24f, // Xe 54
+    3.34f, // Cs 55
+    2.78f, // Ba 56
+    2.74f, // La 57
+    2.70f, // Ce 58
+    2.67f, // Pr 59
+    2.64f, // Nd 60
+    2.62f, // Pm 61
+    2.59f, // Sm 62
+    2.56f, // Eu 63
+    2.54f, // Gd 64
+    2.51f, // Tb 65
+    2.49f, // Dy 66
+    2.47f, // Ho 67
+    2.45f, // Er 68
+    2.42f, // Tm 69
+    2.40f, // Yb 70
+    2.25f, // Lu 71
+    2.16f, // Hf 72
+    2.09f, // Ta 73
+    2.02f, // W  74
+    1.97f, // Re 75
+    1.92f, // Os 76
+    1.87f, // Ir 77
+    1.83f, // Pt 78
+    1.79f, // Au 79
+    1.76f, // Hg 80
+    2.08f, // Tl 81
+    1.81f, // Pb 82
+    1.63f, // Bi 83
+    1.53f, // Po 84
+    1.43f, // At 85
+    1.34f, // Rn 86
+    2.60f, // Fr 87 (using covalent radius just to get a number here)
+    2.15f, // Ra 88 (88-99 from "http://en.wikipedia.org/wiki/Atomic_radii_of_the_elements_(data_page)", which references J.C. Slater, J. Chem. Phys. 1964, 41, 3199.)
+    1.95f, // Ac 89
+    1.80f, // Th 90
+    1.80f, // Pa 91
+    1.75f, // U  92
+    1.75f, // Np 93
+    1.75f, // Pu 94
+    1.75f, // Am 95
+    1.74f, // Cm 96 (96-99 use metallic radius to just get a number)
+    1.70f, // Bk 97
+    1.86f, // Cf 98
+    1.86f, // Es 99
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f,
+    .1f
+};
+
+// From JMol / OpenBabel
+/// van der Waals radii in Angstroms
+const float g_vdwRadii[NUM_ELEMENTS+1] = {
+    1.0,    //   0  ?
+    1.200f, //   1  H
+    1.400f, //   2  He
+    1.820f, //   3  Li
+    1.700f, //   4  Be
+    2.080f, //   5  B
+    1.950f, //   6  C
+    1.850f, //   7  N
+    1.700f, //   8  O
+    1.730f, //   9  F
+    1.540f, //  10  Ne
+    2.270f, //  11  Na
+    1.730f, //  12  Mg
+    2.050f, //  13  Al
+    2.100f, //  14  Si
+    2.080f, //  15  P
+    2.000f, //  16  S
+    1.970f, //  17  Cl
+    1.880f, //  18  Ar
+    2.750f, //  19  K
+    1.973f, //  20  Ca
+    1.700f, //  21  Sc
+    1.700f, //  22  Ti
+    1.700f, //  23  V
+    1.700f, //  24  Cr
+    1.700f, //  25  Mn
+    1.700f, //  26  Fe
+    1.700f, //  27  Co
+    1.630f, //  28  Ni
+    1.400f, //  29  Cu
+    1.390f, //  30  Zn
+    1.870f, //  31  Ga
+    1.700f, //  32  Ge
+    1.850f, //  33  As
+    1.900f, //  34  Se
+    2.100f, //  35  Br
+    2.020f, //  36  Kr
+    1.700f, //  37  Rb
+    1.700f, //  38  Sr
+    1.700f, //  39  Y
+    1.700f, //  40  Zr
+    1.700f, //  41  Nb
+    1.700f, //  42  Mo
+    1.700f, //  43  Tc
+    1.700f, //  44  Ru
+    1.700f, //  45  Rh
+    1.630f, //  46  Pd
+    1.720f, //  47  Ag
+    1.580f, //  48  Cd
+    1.930f, //  49  In
+    2.170f, //  50  Sn
+    2.200f, //  51  Sb
+    2.060f, //  52  Te
+    2.150f, //  53  I
+    2.160f, //  54  Xe
+    1.700f, //  55  Cs
+    1.700f, //  56  Ba
+    1.700f, //  57  La
+    1.700f, //  58  Ce
+    1.700f, //  59  Pr
+    1.700f, //  60  Nd
+    1.700f, //  61  Pm
+    1.700f, //  62  Sm
+    1.700f, //  63  Eu
+    1.700f, //  64  Gd
+    1.700f, //  65  Tb
+    1.700f, //  66  Dy
+    1.700f, //  67  Ho
+    1.700f, //  68  Er
+    1.700f, //  69  Tm
+    1.700f, //  70  Yb
+    1.700f, //  71  Lu
+    1.700f, //  72  Hf
+    1.700f, //  73  Ta
+    1.700f, //  74  W
+    1.700f, //  75  Re
+    1.700f, //  76  Os
+    1.700f, //  77  Ir
+    1.720f, //  78  Pt
+    1.660f, //  79  Au
+    1.550f, //  80  Hg
+    1.960f, //  81  Tl
+    2.020f, //  82  Pb
+    1.700f, //  83  Bi
+    1.700f, //  84  Po
+    1.700f, //  85  At
+    1.700f, //  86  Rn
+    1.700f, //  87  Fr
+    1.700f, //  88  Ra
+    1.700f, //  89  Ac
+    1.700f, //  90  Th
+    1.700f, //  91  Pa
+    1.860f, //  92  U
+    1.700f, //  93  Np
+    1.700f, //  94  Pu
+    1.700f, //  95  Am
+    1.700f, //  96  Cm
+    1.700f, //  97  Bk
+    1.700f, //  98  Cf
+    1.700f, //  99  Es
+    1.700f, // 100  Fm
+    1.700f, // 101  Md
+    1.700f, // 102  No
+    1.700f, // 103  Lr
+    1.700f, // 104  Rf
+    1.700f, // 105  Db
+    1.700f, // 106  Sg
+    1.700f, // 107  Bh
+    1.700f, // 108  Hs
+    1.700f, // 109  Mt
+};
+
+const char * g_elementNames[NUM_ELEMENTS+1] = {
+    "?",    //   0  ?
+    "H",    //   1  H
+    "He",   //   2  He
+    "Li",   //   3  Li
+    "Be",   //   4  Be
+    "B",    //   5  B
+    "C",    //   6  C
+    "N",    //   7  N
+    "O",    //   8  O
+    "F",    //   9  F
+    "Ne",   //  10  Ne
+    "Na",   //  11  Na
+    "Mg",   //  12  Mg
+    "Al",   //  13  Al
+    "Si",   //  14  Si
+    "P",    //  15  P
+    "S",    //  16  S
+    "Cl",   //  17  Cl
+    "Ar",   //  18  Ar
+    "K",    //  19  K
+    "Ca",   //  20  Ca
+    "Sc",   //  21  Sc
+    "Ti",   //  22  Ti
+    "V",    //  23  V
+    "Cr",   //  24  Cr
+    "Mn",   //  25  Mn
+    "Fe",   //  26  Fe
+    "Co",   //  27  Co
+    "Ni",   //  28  Ni
+    "Cu",   //  29  Cu
+    "Zn",   //  30  Zn
+    "Ga",   //  31  Ga
+    "Ge",   //  32  Ge
+    "As",   //  33  As
+    "Se",   //  34  Se
+    "Br",   //  35  Br
+    "Kr",   //  36  Kr
+    "Rb",   //  37  Rb
+    "Sr",   //  38  Sr
+    "Y",    //  39  Y
+    "Zr",   //  40  Zr
+    "Nb",   //  41  Nb
+    "Mo",   //  42  Mo
+    "Tc",   //  43  Tc
+    "Ru",   //  44  Ru
+    "Rh",   //  45  Rh
+    "Pd",   //  46  Pd
+    "Ag",   //  47  Ag
+    "Cd",   //  48  Cd
+    "In",   //  49  In
+    "Sn",   //  50  Sn
+    "Sb",   //  51  Sb
+    "Te",   //  52  Te
+    "I",    //  53  I
+    "Xe",   //  54  Xe
+    "Cs",   //  55  Cs
+    "Ba",   //  56  Ba
+    "La",   //  57  La
+    "Ce",   //  58  Ce
+    "Pr",   //  59  Pr
+    "Nd",   //  60  Nd
+    "Pm",   //  61  Pm
+    "Sm",   //  62  Sm
+    "Eu",   //  63  Eu
+    "Gd",   //  64  Gd
+    "Tb",   //  65  Tb
+    "Dy",   //  66  Dy
+    "Ho",   //  67  Ho
+    "Er",   //  68  Er
+    "Tm",   //  69  Tm
+    "Yb",   //  70  Yb
+    "Lu",   //  71  Lu
+    "Hf",   //  72  Hf
+    "Ta",   //  73  Ta
+    "W",    //  74  W
+    "Re",   //  75  Re
+    "Os",   //  76  Os
+    "Ir",   //  77  Ir
+    "Pt",   //  78  Pt
+    "Au",   //  79  Au
+    "Hg",   //  80  Hg
+    "Tl",   //  81  Tl
+    "Pb",   //  82  Pb
+    "Bi",   //  83  Bi
+    "Po",   //  84  Po
+    "At",   //  85  At
+    "Rn",   //  86  Rn
+    "Fr",   //  87  Fr
+    "Ra",   //  88  Ra
+    "Ac",   //  89  Ac
+    "Th",   //  90  Th
+    "Pa",   //  91  Pa
+    "U",    //  92  U
+    "Np",   //  93  Np
+    "Pu",   //  94  Pu
+    "Am",   //  95  Am
+    "Cm",   //  96  Cm
+    "Bk",   //  97  Bk
+    "Cf",   //  98  Cf
+    "Es",   //  99  Es
+    "Fm",   // 100  Fm
+    "Md",   // 101  Md
+    "No",   // 102  No
+    "Lr",   // 103  Lr
+    "Rf",   // 104  Rf
+    "Db",   // 105  Db
+    "Sg",   // 106  Sg
+    "Bh",   // 107  Bh
+    "Hs",   // 108  Hs
+    "Mt",   // 109  Mt
+};
Index: vtkvis/tags/1.8.4/Outline.cpp
===================================================================
--- vtkvis/tags/1.8.4/Outline.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Outline.cpp	(revision 6184)
@@ -0,0 +1,96 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkStructuredGrid.h>
+#include <vtkPolyData.h>
+#include <vtkOutlineFilter.h>
+#include <vtkStructuredGridOutlineFilter.h>
+
+#include "Outline.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Outline::Outline() :
+    GraphicsObject()
+{
+}
+
+Outline::~Outline()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Outline for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Outline with NULL DataSet");
+#endif
+}
+
+/**
+ * \brief Create and initialize a VTK Prop to render a mesh
+ */
+void Outline::initProp()
+{
+    GraphicsObject::initProp();
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void Outline::update()
+{
+    if (_dataSet == NULL) {
+        return;
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+    vtkStructuredGrid *sg = vtkStructuredGrid::SafeDownCast(ds);
+    if (sg != NULL) {
+        vtkSmartPointer<vtkStructuredGridOutlineFilter> ofilter = vtkSmartPointer<vtkStructuredGridOutlineFilter>::New();
+        ofilter->SetInputData(ds);
+        ofilter->ReleaseDataFlagOn();
+        _pdMapper->SetInputConnection(ofilter->GetOutputPort());
+    } else {
+        vtkSmartPointer<vtkOutlineFilter> ofilter = vtkSmartPointer<vtkOutlineFilter>::New();
+        ofilter->SetInputData(ds);
+        ofilter->ReleaseDataFlagOn();
+        _pdMapper->SetInputConnection(ofilter->GetOutputPort());
+    }
+
+    initProp();
+
+    setVisibility(_dataSet->getVisibility());
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+
+#ifdef WANT_TRACE
+    double *b = getBounds();
+    TRACE("bounds: %g %g %g %g %g %g", b[0], b[1], b[2], b[3], b[4], b[5]);
+#endif
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Outline::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_pdMapper != NULL) {
+        _pdMapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Outline.h
===================================================================
--- vtkvis/tags/1.8.4/Outline.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Outline.h	(revision 6184)
@@ -0,0 +1,46 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_OUTLINE_H
+#define VTKVIS_OUTLINE_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+
+#include "GraphicsObject.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK Wire outline of DataSet
+ *
+ * This class creates a wireframe box around the DataSet bounds
+ */
+class Outline : public GraphicsObject {
+public:
+    Outline();
+    virtual ~Outline();
+
+    virtual const char *getClassName() const
+    {
+        return "Outline";
+    }
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+private:
+    virtual void initProp();
+    virtual void update();
+
+    vtkSmartPointer<vtkPolyDataMapper> _pdMapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/PPMWriter.cpp
===================================================================
--- vtkvis/tags/1.8.4/PPMWriter.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/PPMWriter.cpp	(revision 6184)
@@ -0,0 +1,171 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <sys/uio.h>
+
+#include "Trace.h"
+#include "PPMWriter.h"
+#ifdef USE_THREADS
+#include "ResponseQueue.h"
+#endif
+
+using namespace VtkVis;
+
+#ifdef USE_THREADS
+
+/**
+ * \brief Writes image data as PPM binary data to the client.
+ *
+ * The PPM binary format is very simple.
+ *
+ *     P6 w h 255\n
+ *     3-byte RGB pixel data.
+ *
+ * The client (using the TkImg library) will do less work to unpack this 
+ * format, as opposed to BMP or PNG.
+ *
+ * Note that currently the image data has bottom to top scanlines.  This 
+ * routine could be made even simpler (faster) if the image data had top 
+ * to bottom scanlines.
+ *
+ * \param[in] queue Pointer to ResponseQueue to write to
+ * \param[in] cmdName Command name to send (byte length will be appended)
+ * \param[in] data Image data
+ * \param[in] width Width of image in pixels
+ * \param[in] height Height of image in pixels
+ */
+void
+VtkVis::queuePPM(ResponseQueue *queue, const char *cmdName, 
+                 const unsigned char *data, int width, int height)
+{
+#define PPM_MAXVAL 255
+    char header[200];
+
+    TRACE("Entering (%dx%d)\n", width, height);
+    // Generate the PPM binary file header
+    snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height, 
+             PPM_MAXVAL);
+
+    size_t headerLength = strlen(header);
+    size_t dataLength = width * height * 3;
+
+    char command[200];
+    snprintf(command, sizeof(command), "%s %lu\n", cmdName, 
+             (unsigned long)headerLength + dataLength);
+
+    size_t cmdLength;
+    cmdLength = strlen(command);
+
+    size_t length;
+    unsigned char *mesg;
+
+    length = headerLength + dataLength + cmdLength;
+    mesg = (unsigned char *)malloc(length);
+    if (mesg == NULL) {
+        ERROR("can't allocate %ld bytes for the image message", length);
+        return;
+    }
+    memcpy(mesg, command, cmdLength);
+    memcpy(mesg + cmdLength, header, headerLength);
+
+    size_t bytesPerRow = width * 3;
+    unsigned char *destRowPtr = mesg + length - bytesPerRow;
+    int y;
+    const unsigned char *srcRowPtr = data;
+    for (y = 0; y < height; y++) {
+        memcpy(destRowPtr, srcRowPtr, bytesPerRow);
+        srcRowPtr += bytesPerRow;
+        destRowPtr -= bytesPerRow;
+    }
+
+    Response *response;
+    if (strncmp(cmdName, "nv>legend", 9) == 0) {
+        response = new Response(Response::LEGEND);
+    } else {
+        response = new Response(Response::IMAGE);
+    }
+    response->setMessage(mesg, length, Response::DYNAMIC);
+    queue->enqueue(response);
+    TRACE("Leaving (%dx%d)\n", width, height);
+}
+#else
+
+/**
+ * \brief Writes image data as PPM binary data to the client.
+ *
+ * The PPM binary format is very simple.
+ *
+ *     P6 w h 255\n
+ *     3-byte RGB pixel data.
+ *
+ * The client (using the TkImg library) will do less work to unpack this 
+ * format, as opposed to BMP or PNG.
+ *
+ * Note that currently the image data has bottom to top scanlines.  This 
+ * routine could be made even simpler (faster) if the image data had top 
+ * to bottom scanlines.
+ *
+ * \param[in] fd File descriptor that will be written to
+ * \param[in] cmdName Command name to send (byte length will be appended)
+ * \param[in] data Image data
+ * \param[in] width Width of image in pixels
+ * \param[in] height Height of image in pixels
+ */
+void
+VtkVis::writePPM(int fd, const char *cmdName, 
+                 const unsigned char *data, int width, int height)
+{
+#define PPM_MAXVAL 255
+    char header[200];
+
+    TRACE("Entering (%dx%d)\n", width, height);
+    // Generate the PPM binary file header
+    snprintf(header, sizeof(header), "P6 %d %d %d\n", width, height,
+             PPM_MAXVAL);
+
+    size_t headerLength = strlen(header);
+    size_t dataLength = width * height * 3;
+
+    char command[200];
+    snprintf(command, sizeof(command), "%s %lu\n", cmdName, 
+             (unsigned long)headerLength + dataLength);
+
+    size_t nRecs = height + 2;
+
+    struct iovec *iov;
+    iov = (struct iovec *)malloc(sizeof(struct iovec) * nRecs);
+
+    // Write the command, then the image header and data.
+    // Command
+    iov[0].iov_base = command;
+    iov[0].iov_len = strlen(command);
+    // Header of image data
+    iov[1].iov_base = header;
+    iov[1].iov_len = headerLength;
+
+    // Image data
+    size_t bytesPerRow = width * 3;
+    int y;
+    const unsigned char *srcRowPtr = data;
+    for (y = height + 1; y >= 2; y--) {
+        iov[y].iov_base = srcRowPtr;
+        iov[y].iov_len = bytesPerRow;
+        srcRowPtr += bytesPerRow;
+    }
+    if (writev(fd, iov, nRecs) < 0) {
+        ERROR("write failed: %s\n", strerror(errno));
+    }
+    free(iov);
+
+    TRACE("Leaving (%dx%d)\n", width, height);
+}
+
+#endif /*USE_THREADS*/
Index: vtkvis/tags/1.8.4/PPMWriter.h
===================================================================
--- vtkvis/tags/1.8.4/PPMWriter.h	(revision 6184)
+++ vtkvis/tags/1.8.4/PPMWriter.h	(revision 6184)
@@ -0,0 +1,26 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#ifndef VTKVIS_PPMWRITER_H
+#define VTKVIS_PPMWRITER_H
+
+#ifdef USE_THREADS
+#include "ResponseQueue.h"
+#endif
+
+namespace VtkVis {
+#ifdef USE_THREADS
+extern
+void queuePPM(ResponseQueue *queue, const char *cmdName, 
+              const unsigned char *data, int width, int height);
+#else
+extern
+void writePPM(int fd, const char *cmdName, 
+              const unsigned char *data, int width, int height);
+#endif
+}
+#endif
Index: vtkvis/tags/1.8.4/Parallelepiped.cpp
===================================================================
--- vtkvis/tags/1.8.4/Parallelepiped.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Parallelepiped.cpp	(revision 6184)
@@ -0,0 +1,117 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkPolyData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkTransform.h>
+#include <vtkTransformPolyDataFilter.h>
+#include <vtkCubeSource.h>
+#include <vtkReverseSense.h>
+#include <vtkVector.h>
+
+#include "Parallelepiped.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Parallelepiped::Parallelepiped() :
+    Shape()
+{
+}
+
+Parallelepiped::~Parallelepiped()
+{
+    TRACE("Deleting Parallelepiped");
+}
+
+void Parallelepiped::update()
+{
+    if (_box == NULL) {
+	_box = vtkSmartPointer<vtkCubeSource>::New();
+        _box->SetCenter(0.5, 0.5, 0.5);
+        _box->SetXLength(1);
+        _box->SetYLength(1);
+        _box->SetZLength(1);
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    if (_transform == NULL) {
+        _transform = vtkSmartPointer<vtkTransform>::New();
+    }
+
+    vtkSmartPointer<vtkTransformPolyDataFilter> filter = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+    filter->SetInputConnection(_box->GetOutputPort());
+    filter->SetTransform(_transform);
+    _pdMapper->SetInputConnection(filter->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+/**
+ * \brief Create parallelepiped from 3 basis vectors
+ * 
+ * This method expects the order of vectors to define a right-handed coordinate
+ * system, so ((vec1 cross vec2) dot vec3) should be positive.
+ */
+void Parallelepiped::setVectors(double vec1[3], double vec2[3], double vec3[3])
+{
+    vtkVector3d v1(vec1);
+    vtkVector3d v2(vec2);
+    vtkVector3d v3(vec3);
+    vtkVector3d tmp = v1.Cross(v2);
+    if (tmp.Dot(v3) < 0) {
+        // Got left-handed system, convert to right-handed
+        v2.Set(vec1[0], vec1[1], vec1[2]);
+        v1.Set(vec2[0], vec2[1], vec2[2]);
+    }
+    TRACE("v1: %g %g %g", v1[0], v1[1], v1[2]);
+    TRACE("v2: %g %g %g", v2[0], v2[1], v2[2]);
+    TRACE("v3: %g %g %g", v3[0], v3[1], v3[2]);
+    double matrix[16];
+    memset(matrix, 0, sizeof(double)*16);
+    matrix[0] = v1[0];
+    matrix[4] = v1[1];
+    matrix[8] = v1[2];
+    matrix[1] = v2[0];
+    matrix[5] = v2[1];
+    matrix[9] = v2[2];
+    matrix[2] = v3[0];
+    matrix[6] = v3[1];
+    matrix[10] = v3[2];
+    matrix[15] = 1;
+    _transform->SetMatrix(matrix);
+}
+
+void Parallelepiped::flipNormals(bool state)
+{
+    if (_box == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_box->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_box->GetOutputPort());
+    }
+}
Index: vtkvis/tags/1.8.4/Parallelepiped.h
===================================================================
--- vtkvis/tags/1.8.4/Parallelepiped.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Parallelepiped.h	(revision 6184)
@@ -0,0 +1,60 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_PARALLELEPIPED_H
+#define VTKVIS_PARALLELEPIPED_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkCubeSource.h>
+#include <vtkTransform.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData Parallelepiped
+ *
+ * This class creates a mesh parallelepiped
+ */
+class Parallelepiped : public Shape
+{
+public:
+    Parallelepiped();
+    virtual ~Parallelepiped();
+
+    virtual const char *getClassName() const
+    {
+        return "Parallelepiped";
+    }
+
+    void setVectors(double vec1[3], double vec2[3], double vec3[3]);
+
+    void setSize(double xlen, double ylen, double zlen)
+    {
+        if (_box != NULL) {
+            _box->SetXLength(xlen);
+	    _box->SetYLength(ylen);
+	    _box->SetZLength(zlen);
+	}
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkCubeSource> _box;
+    vtkSmartPointer<vtkTransform> _transform;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/PolyData.cpp
===================================================================
--- vtkvis/tags/1.8.4/PolyData.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/PolyData.cpp	(revision 6184)
@@ -0,0 +1,559 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkPolyData.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkTransform.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkDataSetSurfaceFilter.h>
+#include <vtkVertexGlyphFilter.h>
+#include <vtkLookupTable.h>
+
+#include "PolyData.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+PolyData::PolyData() :
+    GraphicsObject(),
+    _colorMap(NULL),
+    _colorMode(COLOR_CONSTANT),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL),
+    _cloudStyle(CLOUD_MESH)
+{
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+PolyData::~PolyData()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting PolyData for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting PolyData with NULL DataSet");
+#endif
+}
+
+void PolyData::setDataSet(DataSet *dataSet,
+                          Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void PolyData::update()
+{
+    if (_dataSet == NULL) {
+        return;
+    }
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    if (_mapper == NULL) {
+        _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _mapper->SetResolveCoincidentTopologyToPolygonOffset();
+        // If there are color scalars, use them without lookup table (if scalar visibility is on)
+        _mapper->SetColorModeToDefault();
+        // Use Point data if available, else cell data
+        _mapper->SetScalarModeToDefault();
+        _mapper->ScalarVisibilityOff();
+    }
+
+    vtkPolyData *pd = vtkPolyData::SafeDownCast(ds);
+    if (pd) {
+        TRACE("Points: %d Verts: %d Lines: %d Polys: %d Strips: %d",
+              pd->GetNumberOfPoints(),
+              pd->GetNumberOfVerts(),
+              pd->GetNumberOfLines(),
+              pd->GetNumberOfPolys(),
+              pd->GetNumberOfStrips());
+    }
+    bool hasNormals = false;
+    if ((ds->GetPointData() != NULL &&
+         ds->GetPointData()->GetNormals() != NULL) ||
+        (ds->GetCellData() != NULL &&
+         ds->GetCellData()->GetNormals() != NULL)) {
+        hasNormals = true;
+    }
+
+    if (_dataSet->isCloud()) {
+        // DataSet is a point cloud
+        PrincipalPlane plane;
+        double offset;
+        if (_cloudStyle == CLOUD_POINTS ||
+            _dataSet->numDimensions() < 2 || ds->GetNumberOfPoints() < 3) { // 0D or 1D or not enough points to mesh
+            vtkSmartPointer<vtkVertexGlyphFilter> vgf = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+            vgf->SetInputData(ds);
+            _mapper->SetInputConnection(vgf->GetOutputPort());
+        } else if (_dataSet->is2D(&plane, &offset)) {
+            vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+            if (plane == PLANE_ZY) {
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->RotateWXYZ(90, 0, 1, 0);
+                if (offset != 0.0) {
+                    trans->Translate(-offset, 0, 0);
+                }
+                mesher->SetTransform(trans);
+            } else if (plane == PLANE_XZ) {
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->RotateWXYZ(-90, 1, 0, 0);
+                if (offset != 0.0) {
+                    trans->Translate(0, -offset, 0);
+                }
+                mesher->SetTransform(trans);
+            } else if (offset != 0.0) {
+                // XY with Z offset
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->Translate(0, 0, -offset);
+                mesher->SetTransform(trans);
+            }
+            mesher->SetInputData(ds);
+            mesher->ReleaseDataFlagOn();
+            mesher->Update();
+            vtkPolyData *outpd = mesher->GetOutput();
+            TRACE("Delaunay2D Verts: %d Lines: %d Polys: %d Strips: %d",
+                  outpd->GetNumberOfVerts(),
+                  outpd->GetNumberOfLines(),
+                  outpd->GetNumberOfPolys(),
+                  outpd->GetNumberOfStrips());
+            if (outpd->GetNumberOfPolys() == 0) {
+                WARN("Delaunay2D mesher failed");
+                vtkSmartPointer<vtkVertexGlyphFilter> vgf = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+                vgf->SetInputData(ds);
+                _mapper->SetInputConnection(vgf->GetOutputPort());
+            } else {
+                vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+                normalFilter->SetInputConnection(mesher->GetOutputPort());
+                _mapper->SetInputConnection(normalFilter->GetOutputPort());
+            }
+        } else {
+            vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+            mesher->SetInputData(ds);
+            mesher->ReleaseDataFlagOn();
+            mesher->Update();
+            vtkUnstructuredGrid *grid = mesher->GetOutput();
+            TRACE("Delaunay3D Cells: %d",
+                  grid->GetNumberOfCells());
+            if (grid->GetNumberOfCells() == 0) {
+                WARN("Delaunay3D mesher failed");
+                vtkSmartPointer<vtkVertexGlyphFilter> vgf = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+                vgf->SetInputData(ds);
+                _mapper->SetInputConnection(vgf->GetOutputPort());
+            } else {
+                // Delaunay3D returns an UnstructuredGrid, so feed it 
+                // through a surface filter to get the grid boundary 
+                // as a PolyData
+                vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                gf->UseStripsOn();
+                gf->ReleaseDataFlagOn();
+                gf->SetInputConnection(mesher->GetOutputPort());
+                vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+                normalFilter->SetInputConnection(gf->GetOutputPort());
+                _mapper->SetInputConnection(normalFilter->GetOutputPort());
+            }
+        }
+    } else if (pd) {
+        // DataSet is a vtkPolyData with cells
+        if (hasNormals) {
+            _mapper->SetInputData(pd);
+        } else {
+            vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+            normalFilter->SetInputData(pd);
+            _mapper->SetInputConnection(normalFilter->GetOutputPort());
+        }
+    } else {
+        // DataSet is NOT a vtkPolyData
+        TRACE("DataSet is not a PolyData");
+        vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+        //gf->UseStripsOn();
+        gf->ReleaseDataFlagOn();
+        gf->SetInputData(ds);
+        vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+        normalFilter->SetInputConnection(gf->GetOutputPort());
+        _mapper->SetInputConnection(normalFilter->GetOutputPort());
+    }
+
+    setInterpolateBeforeMapping(true);
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+        if (_dataSet->getActiveScalarsName() != NULL) {
+            int numComp;
+            _dataSet->getFieldInfo(_dataSet->getActiveScalarsName(),
+                                   _dataSet->getActiveScalarsType(),
+                                   &numComp);
+            // If DataSet is a PolyData with a scalar field, or has color scalars
+            if (pd != NULL || numComp > 1) {
+                if (numComp > 1) {
+                    // If we have color scalars, use triangle interpolation
+                    setInterpolateBeforeMapping(false);
+                }
+                setColorMode(COLOR_BY_SCALAR);
+            } else {
+                setColorMode(_colorMode);
+            }
+        } else {
+            setColorMode(_colorMode);
+        }
+    } else {
+        double *rangePtr = _colorFieldRange;
+        if (_colorFieldRange[0] > _colorFieldRange[1]) {
+            rangePtr = NULL;
+        }
+        setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+    }
+
+    initProp();
+    getActor()->SetMapper(_mapper);
+    _mapper->Update();
+#ifdef WANT_TRACE
+    double *b = getBounds();
+    TRACE("bounds: %g %g %g %g %g %g", b[0], b[1], b[2], b[3], b[4], b[5]);
+#endif
+}
+
+void PolyData::setInterpolateBeforeMapping(bool state)
+{
+    if (_mapper != NULL) {
+        _mapper->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
+    }
+}
+
+void PolyData::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                         _dataSet->getActiveVectorsName(),
+                                         3);
+        for (int i = 0; i < 3; i++) {
+            renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                             _dataSet->getActiveVectorsName(),
+                                             3, i);
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+}
+
+void PolyData::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void PolyData::setColorMode(ColorMode mode,
+                            const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void PolyData::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                            const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _mapper == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        _mapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        _mapper->SetScalarModeToUseCellFieldData();
+        break;
+    case DataSet::FIELD_DATA:
+        _mapper->SetScalarModeToUseFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (name != NULL && strlen(name) > 0) {
+        _mapper->SelectColorArray(name);
+    } else {
+        _mapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _mapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _mapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void PolyData::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void PolyData::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_mapper != NULL) {
+            _mapper->UseLookupTableScalarRangeOn();
+            _mapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void PolyData::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_mapper != NULL) {
+        _mapper->SetClippingPlanes(planes);
+    }
+}
+
+void PolyData::setCloudStyle(CloudStyle style)
+{
+    if (style != _cloudStyle) {
+        _cloudStyle = style;
+        if (_dataSet != NULL) {
+            update();
+        }
+    }
+}
Index: vtkvis/tags/1.8.4/PolyData.h
===================================================================
--- vtkvis/tags/1.8.4/PolyData.h	(revision 6184)
+++ vtkvis/tags/1.8.4/PolyData.h	(revision 6184)
@@ -0,0 +1,99 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_POLYDATA_H
+#define VTKVIS_POLYDATA_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkLookupTable.h>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK Mesh (Polygon data)
+ *
+ * This class creates a boundary mesh of a DataSet
+ */
+class PolyData : public GraphicsObject {
+public:
+    enum CloudStyle {
+        CLOUD_MESH,
+        CLOUD_POINTS
+    };
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+
+    PolyData();
+    virtual ~PolyData();
+
+    virtual const char *getClassName() const
+    {
+        return "PolyData";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setCloudStyle(CloudStyle style);
+
+    void setInterpolateBeforeMapping(bool state);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    virtual void update();
+
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    CloudStyle _cloudStyle;
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkPolyDataMapper> _mapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Polygon.cpp
===================================================================
--- vtkvis/tags/1.8.4/Polygon.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Polygon.cpp	(revision 6184)
@@ -0,0 +1,69 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkRegularPolygonSource.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkReverseSense.h>
+
+#include "Polygon.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Polygon::Polygon() :
+    Shape()
+{
+}
+
+Polygon::~Polygon()
+{
+    TRACE("Deleting Polygon");
+}
+
+void Polygon::update()
+{
+    if (_polygon == NULL) {
+	_polygon = vtkSmartPointer<vtkRegularPolygonSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    vtkSmartPointer<vtkPolyDataNormals> normalFilter = vtkSmartPointer<vtkPolyDataNormals>::New();
+    normalFilter->SetInputConnection(_polygon->GetOutputPort());
+
+    _pdMapper->SetInputConnection(_polygon->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+void Polygon::flipNormals(bool state)
+{
+    if (_polygon == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_polygon->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_polygon->GetOutputPort());
+    }
+    _pdMapper->Update();
+}
+
Index: vtkvis/tags/1.8.4/Polygon.h
===================================================================
--- vtkvis/tags/1.8.4/Polygon.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Polygon.h	(revision 6184)
@@ -0,0 +1,75 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_POLYGON_H
+#define VTKVIS_POLYGON_H_
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkRegularPolygonSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData regular n-sided polygon
+ *
+ * This class creates a regular n-sided polygon
+ */
+class Polygon : public Shape
+{
+public:
+    Polygon();
+    virtual ~Polygon();
+
+    virtual const char *getClassName() const
+    {
+        return "Polygon";
+    }
+
+    void setNumberOfSides(int numSides)
+    {
+        if (_polygon != NULL) {
+            _polygon->SetNumberOfSides(numSides);
+	}
+    }
+
+    void setCenter(double center[3])
+    {
+        if (_polygon != NULL) {
+            _polygon->SetCenter(center);
+	}
+    }
+
+    void setNormal(double normal[3])
+    {
+        if (_polygon != NULL) {
+            _polygon->SetNormal(normal);
+	}
+    }
+
+    void setRadius(double radius)
+    {
+        if (_polygon != NULL) {
+            _polygon->SetRadius(radius);
+	}
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkRegularPolygonSource> _polygon;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/PseudoColor.cpp
===================================================================
--- vtkvis/tags/1.8.4/PseudoColor.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/PseudoColor.cpp	(revision 6184)
@@ -0,0 +1,532 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+#include <cfloat>
+#include <cstring>
+
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkPolyData.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkProperty.h>
+#include <vtkImageData.h>
+#include <vtkLookupTable.h>
+#include <vtkTransform.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkGaussianSplatter.h>
+#include <vtkExtractVOI.h>
+#include <vtkDataSetSurfaceFilter.h>
+#include <vtkVertexGlyphFilter.h>
+
+#include "PseudoColor.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+PseudoColor::PseudoColor() :
+    GraphicsObject(),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL),
+    _cloudStyle(CLOUD_MESH)
+{
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+PseudoColor::~PseudoColor()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting PseudoColor for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting PseudoColor with NULL DataSet");
+#endif
+}
+
+void PseudoColor::setDataSet(DataSet *dataSet,
+                             Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void PseudoColor::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    // Mapper, actor to render color-mapped data set
+    if (_mapper == NULL) {
+        _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _mapper->SetResolveCoincidentTopologyToPolygonOffset();
+        // Map scalars through lookup table regardless of type
+        _mapper->SetColorModeToMapScalars();
+    }
+
+    _splatter = NULL;
+    if (_dataSet->isCloud()) {
+        // DataSet is a point cloud
+        if (_cloudStyle == CLOUD_POINTS ||
+            _dataSet->numDimensions() < 2 || ds->GetNumberOfPoints() < 3) {
+            vtkSmartPointer<vtkVertexGlyphFilter> vgf = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+            vgf->SetInputData(ds);
+            vgf->ReleaseDataFlagOn();
+            _mapper->SetInputConnection(vgf->GetOutputPort());
+        } else {
+            PrincipalPlane plane;
+            double offset;
+            if (_dataSet->is2D(&plane, &offset)) {
+                if (_cloudStyle == CLOUD_MESH) {
+                    // 2D CLOUD_MESH
+                    vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                    if (plane == PLANE_ZY) {
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->RotateWXYZ(90, 0, 1, 0);
+                        if (offset != 0.0) {
+                            trans->Translate(-offset, 0, 0);
+                        }
+                        mesher->SetTransform(trans);
+                    } else if (plane == PLANE_XZ) {
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->RotateWXYZ(-90, 1, 0, 0);
+                        if (offset != 0.0) {
+                            trans->Translate(0, -offset, 0);
+                        }
+                        mesher->SetTransform(trans);
+                    } else if (offset != 0.0) {
+                        // XY with Z offset
+                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                        trans->Translate(0, 0, -offset);
+                        mesher->SetTransform(trans);
+                    }
+                    mesher->SetInputData(ds);
+                    _mapper->SetInputConnection(mesher->GetOutputPort());
+                } else {
+                    // 2D CLOUD_SPLAT
+                    if (_splatter == NULL) {
+                        _splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                    }
+                    _splatter->SetInputData(ds);
+                    int dims[3];
+                    _splatter->GetSampleDimensions(dims);
+                    TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
+                    vtkSmartPointer<vtkExtractVOI> slicer = vtkSmartPointer<vtkExtractVOI>::New();
+                    if (plane == PLANE_ZY) {
+                        dims[0] = 3;
+                        slicer->SetVOI(1, 1, 0, dims[1]-1, 0, dims[1]-1);
+                    } else if (plane == PLANE_XZ) {
+                        dims[1] = 3;
+                        slicer->SetVOI(0, dims[0]-1, 1, 1, 0, dims[2]-1);
+                    } else {
+                        dims[2] = 3;
+                        slicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, 1, 1);
+                    }
+                    _splatter->SetSampleDimensions(dims);
+                    double bounds[6];
+                    _splatter->Update();
+                    _splatter->GetModelBounds(bounds);
+                    TRACE("Model bounds: %g %g %g %g %g %g",
+                          bounds[0], bounds[1],
+                          bounds[2], bounds[3],
+                          bounds[4], bounds[5]);
+                    slicer->SetInputConnection(_splatter->GetOutputPort());
+                    slicer->SetSampleRate(1, 1, 1);
+                    vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                    gf->UseStripsOn();
+                    gf->SetInputConnection(slicer->GetOutputPort());
+                    _mapper->SetInputConnection(gf->GetOutputPort());
+                }
+            } else {
+                // 3D cloud
+                vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+                mesher->SetInputData(ds);
+                vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                gf->SetInputConnection(mesher->GetOutputPort());
+                _mapper->SetInputConnection(gf->GetOutputPort());
+            }
+        }
+    } else if (vtkPolyData::SafeDownCast(ds) != NULL) {
+        // DataSet is a vtkPolyData with lines and/or polygons
+        _mapper->SetInputData(vtkPolyData::SafeDownCast(ds));
+    } else {
+        TRACE("Generating surface for data set");
+        // DataSet is NOT a vtkPolyData and has cells
+        vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+        gf->SetInputData(ds);
+        _mapper->SetInputConnection(gf->GetOutputPort());
+    }
+
+    setInterpolateBeforeMapping(true);
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+        if (ds->GetPointData()->GetScalars() == NULL &&
+            ds->GetPointData()->GetVectors() != NULL) {
+            TRACE("Setting color mode to vector magnitude");
+            setColorMode(COLOR_BY_VECTOR_MAGNITUDE);
+        } else {
+            TRACE("Setting color mode to scalar");
+            setColorMode(COLOR_BY_SCALAR);
+        }
+    } else {
+        double *rangePtr = _colorFieldRange;
+        if (_colorFieldRange[0] > _colorFieldRange[1]) {
+            rangePtr = NULL;
+        }
+        setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+    }
+
+    initProp();
+    getActor()->SetMapper(_mapper);
+    _mapper->Update();
+#ifdef WANT_TRACE
+    double *b = getBounds();
+    TRACE("bounds: %g %g %g %g %g %g", b[0], b[1], b[2], b[3], b[4], b[5]);
+#endif
+}
+
+void PseudoColor::setCloudStyle(CloudStyle style)
+{
+    if (style != _cloudStyle) {
+        _cloudStyle = style;
+        if (_dataSet != NULL) {
+            update();
+        }
+    }
+}
+
+void PseudoColor::setInterpolateBeforeMapping(bool state)
+{
+    if (_mapper != NULL) {
+        _mapper->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
+    }
+}
+
+void PseudoColor::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                         _dataSet->getActiveVectorsName(),
+                                         3);
+        for (int i = 0; i < 3; i++) {
+            renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                             _dataSet->getActiveVectorsName(),
+                                             3, i);
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+}
+
+void PseudoColor::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void PseudoColor::setColorMode(ColorMode mode,
+                               const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void PseudoColor::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                               const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _mapper == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        _mapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        _mapper->SetScalarModeToUseCellFieldData();
+        break;
+    case DataSet::FIELD_DATA:
+        _mapper->SetScalarModeToUseFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (_splatter != NULL) {
+        if (name != NULL && strlen(name) > 0) {
+            _splatter->SetInputArrayToProcess(0, 0, 0, type, name);
+        }
+        _mapper->SelectColorArray("SplatterValues");
+    } else if (name != NULL && strlen(name) > 0) {
+        _mapper->SelectColorArray(name);
+    } else {
+        _mapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _mapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _mapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void PseudoColor::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void PseudoColor::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_mapper != NULL) {
+            _mapper->UseLookupTableScalarRangeOn();
+            _mapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void PseudoColor::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_mapper != NULL) {
+        _mapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/PseudoColor.h
===================================================================
--- vtkvis/tags/1.8.4/PseudoColor.h	(revision 6184)
+++ vtkvis/tags/1.8.4/PseudoColor.h	(revision 6184)
@@ -0,0 +1,101 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_PSEUDOCOLOR_H
+#define VTKVIS_PSEUDOCOLOR_H
+
+#include <vtkSmartPointer.h>
+#include <vtkLookupTable.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkPlaneCollection.h>
+#include <vtkGaussianSplatter.h>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Color-mapped plot of data set
+ */
+class PseudoColor : public GraphicsObject {
+public:
+    enum CloudStyle {
+        CLOUD_MESH,
+        CLOUD_POINTS,
+        CLOUD_SPLAT
+    };
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+
+    PseudoColor();
+    virtual ~PseudoColor();
+
+    virtual const char *getClassName() const
+    {
+        return "PseudoColor";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setCloudStyle(CloudStyle style);
+
+    void setInterpolateBeforeMapping(bool state);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    virtual void update();
+
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    CloudStyle _cloudStyle;
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkPolyDataMapper> _mapper;
+    vtkSmartPointer<vtkGaussianSplatter> _splatter;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/ReadBuffer.cpp
===================================================================
--- vtkvis/tags/1.8.4/ReadBuffer.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/ReadBuffer.cpp	(revision 6184)
@@ -0,0 +1,185 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#include <cstdlib>
+#include <cerrno>
+#include <cstdio>
+#include <unistd.h>
+#include <cstring>
+
+#include "ReadBuffer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+/**
+ * \param[in] fd File descriptor to read
+ * \param[in] bufferSize Block size to use in internal buffer
+ */
+ReadBuffer::ReadBuffer(int fd, size_t bufferSize) :
+    _bufferSize(bufferSize),
+    _fd(fd),
+    _lastStatus(OK)
+{
+    _bytes = new unsigned char [_bufferSize];
+    flush();
+}
+
+ReadBuffer::~ReadBuffer()
+{
+    TRACE("Deleting ReadBuffer");
+    delete [] _bytes;
+}
+
+/**
+ * \brief Checks if a new line is currently in the buffer.
+ *
+ * \return the index of the character past the new line.
+ */
+size_t
+ReadBuffer::nextNewLine()
+{
+    /* Check for a newline in the current buffer. */
+    unsigned char *ptr = 
+        (unsigned char *)memchr(_bytes + _mark, '\n', _fill - _mark);
+    if (ptr == NULL)
+        return 0;
+    else
+        return (ptr - _bytes + 1);
+}
+
+/**  
+ * \brief Fills the buffer with available data.
+ * 
+ * Any existing data in the buffer is moved to the front of the buffer, 
+ * then the channel is read to fill the rest of the buffer.
+ *
+ * \return If an error occur when reading the channel, then ERROR is
+ * returned. ENDFILE is returned on EOF.  If the buffer can't be filled, 
+ * then CONTINUE is returned.
+ */
+ReadBuffer::BufferStatus
+ReadBuffer::doFill()
+{
+    //TRACE("Enter, mark: %lu fill: %lu", _mark, _fill);
+    if (_mark >= _fill) {
+        flush();                        /* Fully consumed buffer */
+    }
+    if (_mark > 0) {
+        /* Some data has been consumed. Move the unconsumed data to the front
+         * of the buffer. */
+        TRACE("memmove %lu bytes", _fill-_mark);
+        memmove(_bytes, _bytes + _mark, _fill - _mark);
+        _fill -= _mark;
+        _mark = 0;
+    }
+    ssize_t numRead;
+    size_t bytesLeft;
+
+    bytesLeft = _bufferSize - _fill;
+    //TRACE("going to read %lu bytes", bytesLeft);
+    numRead = read(_fd, _bytes + _fill, bytesLeft);
+    if (numRead == 0) {
+        /* EOF */
+        TRACE("EOF found reading buffer");
+        return ReadBuffer::ENDFILE;
+    }
+    if (numRead < 0) {
+        if (errno != EAGAIN) {
+            ERROR("error reading buffer: %s", strerror(errno));
+            return ReadBuffer::ERROR;
+        }
+        TRACE("Short read for buffer");
+        return ReadBuffer::CONTINUE;
+    }
+    _fill += numRead;
+    //TRACE("Read %lu bytes", numRead);
+    return ((size_t)numRead == bytesLeft) 
+        ? ReadBuffer::OK : ReadBuffer::CONTINUE;
+}
+
+/**
+ * \brief Read the requested number of bytes from the buffer.
+
+ * Fails if the requested number of bytes are not immediately 
+ * available. Never should be short. 
+ */
+ReadBuffer::BufferStatus
+ReadBuffer::followingData(unsigned char *out, size_t numBytes)
+{
+    TRACE("Enter");
+    while (numBytes > 0) {
+        size_t bytesLeft;
+
+        bytesLeft = _fill - _mark;
+        if (bytesLeft > 0) {
+            int size;
+
+            /* Pull bytes out of the buffer, updating the mark. */
+            size = (bytesLeft >  numBytes) ? numBytes : bytesLeft;
+            memcpy(out, _bytes + _mark, size);
+            _mark += size;
+            numBytes -= size;
+            out += size;
+        }
+        if (numBytes == 0) {
+            /* Received requested # bytes. */
+            return ReadBuffer::OK;
+        }
+        /* Didn't get enough bytes, need to read some more. */
+        _lastStatus = doFill();
+        if (_lastStatus == ReadBuffer::ERROR ||
+            _lastStatus == ReadBuffer::ENDFILE) {
+            return _lastStatus;
+        }
+    }
+    return ReadBuffer::OK;
+}
+
+/** 
+ * \brief Returns the next available line (terminated by a newline)
+ * 
+ * If insufficient data is in the buffer, then the channel is
+ * read for more data.  If reading the channel results in a
+ * short read, CONTINUE is returned and *numBytesPtr is set to 0.
+ */
+ReadBuffer::BufferStatus
+ReadBuffer::getLine(size_t *numBytesPtr, unsigned char **bytesPtr)
+{
+    TRACE("Enter");
+    *numBytesPtr = 0;
+    *bytesPtr = NULL;
+
+    _lastStatus = ReadBuffer::OK;
+    for (;;) {
+        size_t _newline;
+
+        _newline = nextNewLine();
+        if (_newline > 0) {
+            /* Start of the line. */
+            *bytesPtr = _bytes + _mark;
+            /* Number of bytes in the line. */
+            *numBytesPtr = _newline - _mark;
+            _mark = _newline;
+            return ReadBuffer::OK;
+        }
+        /* Couldn't find a newline, so it may be that we need to read some
+         * more. Check first that last read wasn't a short read. */
+        if (_lastStatus == ReadBuffer::CONTINUE) {
+            /* No complete line just yet. */
+            return ReadBuffer::CONTINUE;
+        }
+        /* Try to add more data to the buffer. */
+        _lastStatus = doFill();
+        if (_lastStatus == ReadBuffer::ERROR ||
+            _lastStatus == ReadBuffer::ENDFILE) {
+            return _lastStatus;
+        }
+        /* OK or CONTINUE */
+    }
+    return ReadBuffer::CONTINUE;
+}
Index: vtkvis/tags/1.8.4/ReadBuffer.h
===================================================================
--- vtkvis/tags/1.8.4/ReadBuffer.h	(revision 6184)
+++ vtkvis/tags/1.8.4/ReadBuffer.h	(revision 6184)
@@ -0,0 +1,84 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#include <cstdlib>
+
+#ifndef VTKVIS_READBUFFER_H
+#define VTKVIS_READBUFFER_H
+
+namespace VtkVis {
+
+/**
+ * \brief Buffered input for reading socket
+ */
+class ReadBuffer
+{
+public:
+    enum BufferStatus {
+        ENDFILE=-1,
+        OK,
+        ERROR,
+        CONTINUE,
+    };
+
+    ReadBuffer(int fd, size_t bufferSize);
+
+    virtual ~ReadBuffer();
+
+    BufferStatus getLine(size_t *numBytesPtr, unsigned char **bytesPtr);
+
+    BufferStatus followingData(unsigned char *out, size_t numBytes);
+
+    /**
+     * \brief Check if buffer contains data ending in a newline
+     * 
+     * \return bool indicating if getLine can be called without blocking 
+     */
+    bool isLineAvailable()
+    {
+        return (nextNewLine() > 0);
+    }
+
+    /**
+     * \brief Get the file descriptor this object reads from
+     */
+    int file()
+    {
+        return _fd;
+    }
+
+    /**
+     * \brief Get the status of the last read
+     */
+    BufferStatus status()
+    {
+        return _lastStatus;
+    }
+
+private:
+    unsigned char *_bytes;		/**< New-ed buffer to hold read
+					 * characters. */
+    size_t _bufferSize;			/**< # of bytes allocated for buffer */
+    size_t _fill;			/**< # of bytes used in the buffer */
+    size_t _mark;			/**< Starting point of the unconsumed
+					 * data in the buffer. */
+    int _fd;				/**< File descriptor to get data. */
+    BufferStatus _lastStatus;           /**< Status of last read operation. */
+
+    BufferStatus doFill();
+
+    void flush()
+    {
+        _fill = _mark = 0;
+    }
+
+    size_t nextNewLine();
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/RenderServer.cpp
===================================================================
--- vtkvis/tags/1.8.4/RenderServer.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/RenderServer.cpp	(revision 6184)
@@ -0,0 +1,624 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+#include <cerrno>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/times.h>
+
+#include <string>
+#include <sstream>
+
+#include <tcl.h>
+
+#include <vtksys/SystemInformation.hxx>
+
+#include "Trace.h"
+#include "ReadBuffer.h"
+#include "RenderServer.h"
+#include "RendererCmd.h"
+#include "Renderer.h"
+#include "PPMWriter.h"
+#include "TGAWriter.h"
+#ifdef USE_THREADS
+#include <pthread.h>
+#include "ResponseQueue.h"
+#endif 
+#include <md5.h>
+
+using namespace VtkVis;
+
+Stats VtkVis::g_stats;
+
+int VtkVis::g_statsFile = -1; ///< Stats output file descriptor.
+int VtkVis::g_fdIn = STDIN_FILENO; ///< Input file descriptor
+int VtkVis::g_fdOut = STDOUT_FILENO; ///< Output file descriptor
+FILE *VtkVis::g_fOut = NULL; ///< Output file handle
+FILE *VtkVis::g_fLog = NULL; ///< Trace logging file handle
+Renderer *VtkVis::g_renderer = NULL; ///< Main render worker
+ReadBuffer *VtkVis::g_inBufPtr = NULL; ///< Socket read buffer
+
+#ifdef USE_THREADS
+static void
+queueFrame(ResponseQueue *queue, vtkUnsignedCharArray *imgData)
+{
+#ifdef DEBUG
+    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
+        double xywh[4];
+        g_renderer->getScreenWorldCoords(xywh);
+        TRACE("Image bbox: %g %g %g %g", 
+              xywh[0], 
+              (xywh[1] + xywh[3]), 
+              (xywh[0] + xywh[2]), 
+              xywh[1]);
+    }
+
+#ifdef RENDER_TARGA
+    writeTGAFile("/tmp/frame.tga",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL);
+#else
+    writeTGAFile("/tmp/frame.tga",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL,
+                 true);
+#endif  /*RENDER_TARGA*/
+
+#else
+    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
+        double xywh[4];
+        g_renderer->getCameraZoomRegion(xywh);
+        std::ostringstream oss;
+        oss.precision(12);
+        // Send upper left and lower right corners as bbox
+        oss << "nv>image -type image -bbox {"
+            << std::scientific
+            << xywh[0] << " "
+            << xywh[1] << " "
+            << xywh[2] << " "
+            << xywh[3] << "} -bytes";
+
+#ifdef RENDER_TARGA
+        queueTGA(queue, oss.str().c_str(),
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL);
+#else
+        queuePPM(queue, oss.str().c_str(),
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight());
+#endif  /*RENDER_TARGA*/
+    } else {
+#ifdef RENDER_TARGA
+        queueTGA(queue, "nv>image -type image -bytes",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL);
+#else
+        queuePPM(queue, "nv>image -type image -bytes",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight());
+#endif  /*RENDER_TARGA*/
+    }
+#endif  /*DEBUG*/
+}
+
+#else
+
+static void
+writeFrame(int fd, vtkUnsignedCharArray *imgData)
+{
+#ifdef DEBUG
+    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
+        double xywh[4];
+        g_renderer->getScreenWorldCoords(xywh);
+        TRACE("Image bbox: %g %g %g %g", 
+              xywh[0], 
+              (xywh[1] + xywh[3]), 
+              (xywh[0] + xywh[2]), 
+              xywh[1]);
+    }
+
+#ifdef RENDER_TARGA
+    writeTGAFile("/tmp/frame.tga",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL);
+#else
+    writeTGAFile("/tmp/frame.tga",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL,
+                 true);
+#endif  /*RENDER_TARGA*/
+
+#else
+    if (g_renderer->getCameraMode() == Renderer::IMAGE) {
+        double xywh[4];
+        g_renderer->getCameraZoomRegion(xywh);
+        std::ostringstream oss;
+        oss.precision(12);
+        // Send upper left and lower right corners as bbox
+        oss << "nv>image -type image -bbox {"
+            << std::scientific
+            << xywh[0] << " "
+            << xywh[1] << " "
+            << xywh[2] << " "
+            << xywh[3] << "} -bytes";
+
+#ifdef RENDER_TARGA
+        writeTGA(fd, oss.str().c_str(),
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL);
+#else
+        writePPM(fd, oss.str().c_str(),
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight());
+#endif  /*RENDER_TARGA*/
+    } else {
+#ifdef RENDER_TARGA
+        writeTGA(fd, "nv>image -type image -bytes",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight(),
+                 TARGA_BYTES_PER_PIXEL);
+#else
+        writePPM(fd, "nv>image -type image -bytes",
+                 imgData->GetPointer(0),
+                 g_renderer->getWindowWidth(),
+                 g_renderer->getWindowHeight());
+#endif  /*RENDER_TARGA*/
+    }
+#endif  /*DEBUG*/
+}
+#endif /*USE_THREADS*/
+
+static int
+sendAck(ClientData clientData, int fdOut)
+{
+    std::ostringstream oss;
+    oss << "nv>ok -token " << g_stats.nCommands <<  "\n";
+    std::string str = oss.str();
+    int nBytes = str.length();
+
+    TRACE("Sending OK for commands through %lu", g_stats.nCommands);
+#ifdef USE_THREADS
+    queueResponse(clientData, str.c_str(), nBytes, Response::VOLATILE, Response::OK);
+#else
+    if (write(fdOut, str.c_str(), nBytes) < 0) {
+        ERROR("write failed: %s", strerror(errno));
+        return -1;
+    }
+#endif
+    return 0;
+}
+
+int
+VtkVis::getStatsFile(Tcl_Obj *objPtr)
+{
+    char fileName[33];
+    char pidstr[200];
+    md5_state_t state;
+    md5_byte_t digest[16];
+    char *string;
+    int length;
+
+    if ((objPtr == NULL) || (g_statsFile >= 0)) {
+        return g_statsFile;
+    }
+    /* By itself the client's key/value pairs aren't unique.  Add in the
+     * process id of this render server. */
+    sprintf(pidstr, "%ld", (long)g_stats.pid);
+
+    /* Create an md5 hash of the key/value pairs and use it as the file name. */
+    string = Tcl_GetStringFromObj(objPtr, &length);
+    md5_init(&state);
+    md5_append(&state, (const md5_byte_t *)string, strlen(string));
+    md5_append(&state, (const md5_byte_t *)pidstr, strlen(pidstr));
+    md5_finish(&state, digest);
+    for (int i = 0; i < 16; i++) {
+        sprintf(fileName + i * 2, "%02x", digest[i]);
+    }
+    std::ostringstream oss;
+    oss << STATSDIR << "/" << fileName;
+    std::string pathStr = oss.str();
+
+    g_statsFile = open(pathStr.c_str(), O_EXCL | O_CREAT | O_WRONLY, 0600);
+    if (g_statsFile < 0) {
+	ERROR("can't open \"%s\": %s", pathStr.c_str(), strerror(errno));
+	return -1;
+    }
+    return g_statsFile;
+}
+
+int
+VtkVis::writeToStatsFile(int f, const char *s, size_t length)
+{
+    if (f >= 0) {
+        ssize_t numWritten;
+
+        numWritten = write(f, s, length);
+        if (numWritten == (ssize_t)length) {
+            close(dup(f));
+        }
+    }
+    return 0;
+}
+
+static int
+serverStats(int code) 
+{
+    double start, finish;
+    char buf[BUFSIZ];
+    Tcl_DString ds;
+    int result;
+    int f;
+
+    {
+	struct timeval tv;
+
+	/* Get ending time.  */
+	gettimeofday(&tv, NULL);
+	finish = CVT2SECS(tv);
+	tv = g_stats.start;
+	start = CVT2SECS(tv);
+    }
+    /* 
+     * Session information:
+     *   - Name of render server
+     *   - Process ID
+     *   - Hostname where server is running
+     *   - Start date of session
+     *   - Start date of session in seconds
+     *   - Number of data sets loaded from client
+     *   - Number of data bytes total loaded from client
+     *   - Number of frames returned
+     *   - Number of bytes total returned (in frames)
+     *   - Number of commands received
+     *   - Total elapsed time of all commands
+     *   - Total elapsed time of session
+     *   - Exit code of vizserver
+     *   - User time 
+     *   - System time
+     *   - User time of children 
+     *   - System time of children 
+     */
+
+    Tcl_DStringInit(&ds);
+    
+    Tcl_DStringAppendElement(&ds, "render_stop");
+    /* renderer */
+    Tcl_DStringAppendElement(&ds, "renderer");
+    Tcl_DStringAppendElement(&ds, "vtkvis");
+    /* pid */
+    Tcl_DStringAppendElement(&ds, "pid");
+    sprintf(buf, "%ld", (long)g_stats.pid);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* host */
+    Tcl_DStringAppendElement(&ds, "host");
+    gethostname(buf, BUFSIZ-1);
+    buf[BUFSIZ-1] = '\0';
+    Tcl_DStringAppendElement(&ds, buf);
+    /* date */
+    Tcl_DStringAppendElement(&ds, "date");
+    strcpy(buf, ctime(&g_stats.start.tv_sec));
+    buf[strlen(buf) - 1] = '\0';
+    Tcl_DStringAppendElement(&ds, buf);
+    /* date_secs */
+    Tcl_DStringAppendElement(&ds, "date_secs");
+    sprintf(buf, "%ld", g_stats.start.tv_sec);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* num_data_sets */
+    Tcl_DStringAppendElement(&ds, "num_data_sets");
+    sprintf(buf, "%lu", (unsigned long)g_stats.nDataSets);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* data_set_bytes */
+    Tcl_DStringAppendElement(&ds, "data_set_bytes");
+    sprintf(buf, "%lu", (unsigned long)g_stats.nDataBytes);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* num_frames */
+    Tcl_DStringAppendElement(&ds, "num_frames");
+    sprintf(buf, "%lu", (unsigned long)g_stats.nFrames);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* frame_bytes */
+    Tcl_DStringAppendElement(&ds, "frame_bytes");
+    sprintf(buf, "%lu", (unsigned long)g_stats.nFrameBytes);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* num_commands */
+    Tcl_DStringAppendElement(&ds, "num_commands");
+    sprintf(buf, "%lu", (unsigned long)g_stats.nCommands);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* cmd_time */
+    Tcl_DStringAppendElement(&ds, "cmd_time");
+    sprintf(buf, "%g", g_stats.cmdTime);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* session_time */
+    Tcl_DStringAppendElement(&ds, "session_time");
+    sprintf(buf, "%g", finish - start);
+    Tcl_DStringAppendElement(&ds, buf);
+    /* status */
+    Tcl_DStringAppendElement(&ds, "status");
+    sprintf(buf, "%d", code);
+    Tcl_DStringAppendElement(&ds, buf);
+    {
+	long clocksPerSec = sysconf(_SC_CLK_TCK);
+	double clockRes = 1.0 / clocksPerSec;
+	struct tms tms;
+
+	memset(&tms, 0, sizeof(tms));
+	times(&tms);
+	/* utime */
+	Tcl_DStringAppendElement(&ds, "utime");
+	sprintf(buf, "%g", tms.tms_utime * clockRes);
+	Tcl_DStringAppendElement(&ds, buf);
+	/* stime */
+	Tcl_DStringAppendElement(&ds, "stime");
+	sprintf(buf, "%g", tms.tms_stime * clockRes);
+	Tcl_DStringAppendElement(&ds, buf);
+	/* cutime */
+	Tcl_DStringAppendElement(&ds, "cutime");
+	sprintf(buf, "%g", tms.tms_cutime * clockRes);
+	Tcl_DStringAppendElement(&ds, buf);
+	/* cstime */
+	Tcl_DStringAppendElement(&ds, "cstime");
+	sprintf(buf, "%g", tms.tms_cstime * clockRes);
+	Tcl_DStringAppendElement(&ds, buf);
+    }
+    Tcl_DStringAppend(&ds, "\n", -1);
+    f = getStatsFile(NULL);
+    result = writeToStatsFile(f, Tcl_DStringValue(&ds), 
+                              Tcl_DStringLength(&ds));
+    close(f);
+    Tcl_DStringFree(&ds);
+    return result;
+}
+
+static void
+initService()
+{
+    // Create a stream associated with the output file descriptor
+    g_fOut = fdopen(g_fdOut, "w");
+    // If running without a socket, use stdout for debugging
+    if (g_fOut == NULL && g_fdOut != STDOUT_FILENO) {
+        g_fdOut = STDOUT_FILENO;
+        g_fOut = stdout;
+    }
+
+    const char *user = getenv("USER");
+    char *logName = NULL;
+    int logNameLen = 0;
+
+    if (user == NULL) {
+        logNameLen = 19+1;
+        logName = (char *)calloc(logNameLen, sizeof(char));
+        strncpy(logName, "/tmp/vtkvis_log.txt", logNameLen);
+    }
+    else {
+        logNameLen = 16+strlen(user)+4+1;
+        logName = (char *)calloc(logNameLen, sizeof(char));
+        strncpy(logName, "/tmp/vtkvis_log_", logNameLen);
+        strncat(logName, user, strlen(user));
+        strncat(logName, ".txt", 4);
+    }
+
+    // open log and map stderr to log file
+    g_fLog = fopen(logName, "w");
+    dup2(fileno(g_fLog), STDERR_FILENO);
+    // If we are writing to socket, map stdout to log 
+    if (g_fdOut != STDOUT_FILENO) { 
+        dup2(fileno(g_fLog), STDOUT_FILENO); 
+    }
+
+    fflush(stdout);
+
+    // clean up malloc'd memory
+    if (logName != NULL) {
+        free(logName);
+    }
+}
+
+static void
+exitService()
+{
+    serverStats(0);
+
+    // close log file
+    if (g_fLog != NULL) {
+        fclose(g_fLog);
+	g_fLog = NULL;
+    }
+}
+
+#ifdef USE_THREADS
+
+static void *
+writerThread(void *clientData)
+{
+    ResponseQueue *queue = (ResponseQueue *)clientData;
+
+    TRACE("Starting writer thread");
+    for (;;) {
+        Response *response = queue->dequeue();
+        if (response == NULL)
+            continue;
+        if (fwrite(response->message(), sizeof(unsigned char), response->length(),
+                   g_fOut) != response->length()) {
+            ERROR("short write while trying to write %ld bytes", 
+                  response->length());
+        }
+        fflush(g_fOut);
+        TRACE("Wrote response of type %d", response->type());
+        delete response;
+        if (feof(g_fOut))
+            break;
+    }    
+    return NULL;
+}
+
+#endif  /*USE_THREADS*/
+
+int
+main(int argc, char *argv[])
+{
+    // Ignore SIGPIPE.  **Is this needed? **
+    signal(SIGPIPE, SIG_IGN);
+
+    while (1) {
+        int c = getopt(argc, argv, "i:o:");
+        if (c == -1) {
+            break;
+        }
+        switch (c) {
+        case 'i': {
+            int fd = atoi(optarg);
+            if (fd >=0 && fd < 5) {
+                g_fdIn = fd;
+            }
+        }
+            break;
+        case 'o': {
+            int fd = atoi(optarg);
+            if (fd >=0 && fd < 5) {
+                g_fdOut = fd;
+            }
+        }
+            break;
+        case '?':
+            break;
+        default:
+            return 1;
+        }
+    }
+
+    initService();
+    initLog();
+
+    memset(&g_stats, 0, sizeof(g_stats));
+    g_stats.pid = getpid();
+    gettimeofday(&g_stats.start, NULL);
+
+    TRACE("Starting VTKVis Server");
+
+    // Sanity check: log descriptor can't be used for client IO
+    if (fileno(g_fLog) == g_fdIn) {
+        ERROR("Invalid input file descriptor");
+        return 1;
+    }
+    if (fileno(g_fLog) == g_fdOut) {
+        ERROR("Invalid output file descriptor");
+        return 1;
+    }
+    TRACE("File descriptors: in %d out %d log %d", g_fdIn, g_fdOut, fileno(g_fLog));
+
+#ifdef WANT_TRACE
+    vtksys::SystemInformation::SetStackTraceOnError(1);
+#endif
+
+    /* This synchronizes the client with the server, so that the client 
+     * doesn't start writing commands before the server is ready. It could
+     * also be used to supply information about the server (version, memory
+     * size, etc). */
+    fprintf(g_fOut, "VtkVis %s (build %s)\n", VTKVIS_VERSION_STRING, SVN_VERSION);
+    fflush(g_fOut);
+
+    g_renderer = new Renderer();
+    g_inBufPtr = new ReadBuffer(g_fdIn, 1<<12);
+
+    Tcl_Interp *interp = Tcl_CreateInterp();
+    ClientData clientData = NULL;
+#ifdef USE_THREADS
+    ResponseQueue *queue = new ResponseQueue();
+    clientData = (ClientData)queue;
+    initTcl(interp, clientData);
+
+    pthread_t writerThreadId;
+    if (pthread_create(&writerThreadId, NULL, &writerThread, queue) < 0) {
+        ERROR("Can't create writer thread: %s", strerror(errno));
+    }
+#else 
+    initTcl(interp, clientData);
+#endif
+
+    vtkSmartPointer<vtkUnsignedCharArray> imgData = 
+        vtkSmartPointer<vtkUnsignedCharArray>::New();
+
+    // Start main server loop
+    for (;;) {
+        if (processCommands(interp, clientData, g_inBufPtr, g_fdOut) < 0)
+            break;
+
+        if (g_renderer->render()) {
+            TRACE("Rendering new frame");
+            g_renderer->getRenderedFrame(imgData);
+#ifdef USE_THREADS
+            queueFrame(queue, imgData);
+#else
+            writeFrame(g_fdOut, imgData);
+#endif
+            g_stats.nFrames++;
+            g_stats.nFrameBytes += imgData->GetDataSize() * imgData->GetDataTypeSize();
+        } else {
+            TRACE("No render required");
+            sendAck(clientData, g_fdOut);
+        }
+
+        if (g_inBufPtr->status() == ReadBuffer::ENDFILE)
+            break;
+    }
+#ifdef USE_THREADS
+    // Writer thread is probably blocked on sem_wait, so cancel instead
+    // of joining
+    if (pthread_cancel(writerThreadId) < 0) {
+        ERROR("Can't cancel writer thread: %s", strerror(errno));
+    } else {
+        TRACE("Cancelled writer thread");
+    }
+
+    TRACE("Deleting ResponseQueue");
+    delete queue;
+    queue = NULL;
+#endif
+
+    TRACE("Stopping Tcl interpreter");
+    exitTcl(interp);
+    interp = NULL;
+
+    TRACE("Deleting ReadBuffer");
+    delete g_inBufPtr;
+    g_inBufPtr = NULL;
+
+    TRACE("Deleting renderer");
+    delete g_renderer;
+    g_renderer = NULL;
+
+    TRACE("Exiting VTKVis Server");
+
+    closeLog();
+    exitService();
+
+    return 0;
+}
Index: vtkvis/tags/1.8.4/RenderServer.h
===================================================================
--- vtkvis/tags/1.8.4/RenderServer.h	(revision 6184)
+++ vtkvis/tags/1.8.4/RenderServer.h	(revision 6184)
@@ -0,0 +1,55 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_RENDERSERVER_H
+#define VTKVIS_RENDERSERVER_H
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+namespace VtkVis {
+
+class Renderer;
+
+#define VTKVIS_VERSION_STRING "1.8.4"
+
+#define MSECS_ELAPSED(t1, t2) \
+    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3) : \
+     (((t2).tv_sec - (t1).tv_sec))*1.0e+3 + (double)((t2).tv_usec - (t1).tv_usec)/1.0e+3)
+
+#define CVT2SECS(x)  ((double)(x).tv_sec) + ((double)(x).tv_usec * 1.0e-6)
+
+#ifndef STATSDIR
+#define STATSDIR	"/var/tmp/visservers"
+#endif
+
+typedef struct {
+    pid_t pid;
+    size_t nDataSets;       /**< # of data sets received */
+    size_t nDataBytes;      /**< # of bytes received as data sets */
+    size_t nFrames;         /**< # of frames sent to client. */
+    size_t nFrameBytes;     /**< # of bytes for all frames. */
+    size_t nCommands;       /**< # of commands executed */
+    double cmdTime;         /**< Elasped time spend executing commands. */
+    struct timeval start;   /**< Start of elapsed time. */
+} Stats;
+
+extern Stats g_stats;
+
+extern int g_fdIn;
+extern int g_fdOut;
+extern FILE *g_fOut;
+extern FILE *g_fLog;
+extern Renderer *g_renderer;
+extern ReadBuffer *g_inBufPtr;
+extern int g_statsFile;
+extern int writeToStatsFile(int f, const char *s, size_t length);
+extern int getStatsFile(Tcl_Obj *objPtr);
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Renderer.cpp
===================================================================
--- vtkvis/tags/1.8.4/Renderer.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Renderer.cpp	(revision 6184)
@@ -0,0 +1,4819 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cfloat>
+#include <cstring>
+#include <cassert>
+#include <cmath>
+
+#ifdef WANT_TRACE
+#include <sys/time.h>
+#endif
+
+#ifdef USE_FONT_CONFIG
+#include <vtkFreeTypeTools.h>
+#endif
+#include <vtkMath.h>
+#include <vtkCamera.h>
+#include <vtkLight.h>
+#include <vtkLightCollection.h>
+#include <vtkCoordinate.h>
+#include <vtkTransform.h>
+#include <vtkCharArray.h>
+#ifdef USE_CUSTOM_AXES
+#include "vtkRpCubeAxesActor.h"
+#else
+#include <vtkCubeAxesActor.h>
+#endif
+#include <vtkProperty.h>
+#include <vtkProperty2D.h>
+#include <vtkPointData.h>
+#include <vtkLookupTable.h>
+#include <vtkTextProperty.h>
+#include <vtkOpenGLRenderWindow.h>
+#include <vtkVersion.h>
+
+#include <GL/gl.h>
+
+#include "Renderer.h"
+#include "RendererGraphicsObjs.h"
+#include "Math.h"
+#include "ColorMap.h"
+#include "Trace.h"
+
+#define MSECS_ELAPSED(t1, t2) \
+    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3) : \
+     (((t2).tv_sec - (t1).tv_sec))*1.0e+3 + (double)((t2).tv_usec - (t1).tv_usec)/1.0e+3)
+
+#define printCameraInfo(camera)                                         \
+do {                                                                    \
+    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g", \
+          (camera)->GetParallelScale(),                                 \
+          (camera)->GetViewAngle(),                                     \
+          (camera)->GetDistance(),                                      \
+          (camera)->GetPosition()[0],                                   \
+          (camera)->GetPosition()[1],                                   \
+          (camera)->GetPosition()[2],                                   \
+          (camera)->GetFocalPoint()[0],                                 \
+          (camera)->GetFocalPoint()[1],                                 \
+          (camera)->GetFocalPoint()[2],                                 \
+          (camera)->GetViewUp()[0],                                     \
+          (camera)->GetViewUp()[1],                                     \
+          (camera)->GetViewUp()[2],                                     \
+          (camera)->GetClippingRange()[0],                              \
+          (camera)->GetClippingRange()[1]);                             \
+} while(0)
+
+using namespace VtkVis;
+
+Renderer::Renderer() :
+    _needsRedraw(true),
+    _needsAxesReset(false),
+    _needsCameraClippingRangeReset(false),
+    _needsCameraReset(false),
+    _windowWidth(500),
+    _windowHeight(500),
+    _cameraMode(PERSPECTIVE),
+    _cameraAspect(ASPECT_NATIVE),
+    _computeOnlyVisibleBounds(false),
+    _imgCameraPlane(PLANE_XY),
+    _imgCameraOffset(0),
+    _cameraZoomRatio(1),
+    _useCumulativeRange(true),
+    _cumulativeRangeOnlyVisible(false)
+{
+    _bgColor[0] = 0;
+    _bgColor[1] = 0;
+    _bgColor[2] = 0;
+    _cameraPan[0] = 0;
+    _cameraPan[1] = 0;
+    _cameraOrientation[0] = 1.0;
+    _cameraOrientation[1] = 0.0;
+    _cameraOrientation[2] = 0.0;
+    _cameraOrientation[3] = 0.0;
+    // clipping planes to prevent overdrawing axes
+    _activeClipPlanes = vtkSmartPointer<vtkPlaneCollection>::New();
+    // bottom
+    _cameraClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
+    _cameraClipPlanes[0]->SetNormal(0, 1, 0);
+    _cameraClipPlanes[0]->SetOrigin(0, 0, 0);
+    if (_cameraMode == IMAGE)
+        _activeClipPlanes->AddItem(_cameraClipPlanes[0]);
+    // left
+    _cameraClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
+    _cameraClipPlanes[1]->SetNormal(1, 0, 0);
+    _cameraClipPlanes[1]->SetOrigin(0, 0, 0);
+    if (_cameraMode == IMAGE)
+        _activeClipPlanes->AddItem(_cameraClipPlanes[1]);
+    // top
+    _cameraClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
+    _cameraClipPlanes[2]->SetNormal(0, -1, 0);
+    _cameraClipPlanes[2]->SetOrigin(0, 1, 0);
+    if (_cameraMode == IMAGE)
+        _activeClipPlanes->AddItem(_cameraClipPlanes[2]);
+    // right
+    _cameraClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
+    _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
+    _cameraClipPlanes[3]->SetOrigin(1, 0, 0);
+    if (_cameraMode == IMAGE)
+        _activeClipPlanes->AddItem(_cameraClipPlanes[3]);
+
+#ifdef USE_FONT_CONFIG
+    vtkFreeTypeTools *typeTools = vtkFreeTypeTools::GetInstance();
+    typeTools->ForceCompiledFontsOff();
+    TRACE("FreeTypeTools impl: %s", typeTools->GetClassName());
+#endif
+
+    _renderer = vtkSmartPointer<vtkRenderer>::New();
+
+    // Global ambient (defaults to 1,1,1)
+    //_renderer->SetAmbient(.2, .2, .2);
+
+    _renderer->AutomaticLightCreationOff();
+
+    vtkSmartPointer<vtkLight> headlight = vtkSmartPointer<vtkLight>::New();
+    headlight->SetLightTypeToHeadlight();
+    headlight->PositionalOff();
+    // Light ambient color defaults to 0,0,0
+    //headlight->SetAmbientColor(1, 1, 1);
+    _renderer->AddLight(headlight);
+
+    vtkSmartPointer<vtkLight> skylight = vtkSmartPointer<vtkLight>::New();
+    skylight->SetLightTypeToCameraLight();
+    skylight->SetPosition(0, 1, 0);
+    skylight->SetFocalPoint(0, 0, 0);
+    skylight->PositionalOff();
+    // Light ambient color defaults to 0,0,0
+    //skylight->SetAmbientColor(1, 1, 1);
+    _renderer->AddLight(skylight);
+
+    _renderer->LightFollowCameraOn();
+    _renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
+#ifdef USE_OFFSCREEN_RENDERING
+    _renderWindow->DoubleBufferOff();
+    _renderWindow->OffScreenRenderingOn();
+#else
+    _renderWindow->SwapBuffersOff();
+#endif
+    _renderWindow->SetSize(_windowWidth, _windowHeight);
+    // Next 2 options needed to support depth peeling
+    _renderWindow->SetAlphaBitPlanes(1);
+    _renderWindow->SetMultiSamples(0);
+    _renderer->SetMaximumNumberOfPeels(100);
+    _renderer->SetUseDepthPeeling(1);
+    _renderer->SetTwoSidedLighting(1);
+    _renderWindow->AddRenderer(_renderer);
+    setViewAngle(_windowHeight);
+    initAxes();
+    //initOrientationMarkers();
+    initCamera();
+    addColorMap("default", ColorMap::getDefault());
+    addColorMap("grayDefault", ColorMap::getGrayDefault());
+    addColorMap("volumeDefault", ColorMap::getVolumeDefault());
+    addColorMap("elementDefault", ColorMap::getElementDefault());
+}
+
+Renderer::~Renderer()
+{
+    TRACE("Enter");
+
+    deleteAllGraphicsObjects<Arc>();
+    deleteAllGraphicsObjects<Arrow>();
+    deleteAllGraphicsObjects<Box>();
+    deleteAllGraphicsObjects<Cone>();
+    deleteAllGraphicsObjects<Contour2D>();
+    deleteAllGraphicsObjects<Contour3D>();
+    deleteAllGraphicsObjects<Cutplane>();
+    deleteAllGraphicsObjects<Cylinder>();
+    deleteAllGraphicsObjects<Disk>();
+    deleteAllGraphicsObjects<Glyphs>();
+    deleteAllGraphicsObjects<Group>();
+    deleteAllGraphicsObjects<HeightMap>();
+    deleteAllGraphicsObjects<Image>();
+    deleteAllGraphicsObjects<ImageCutplane>();
+    deleteAllGraphicsObjects<LIC>();
+    deleteAllGraphicsObjects<Line>();
+    deleteAllGraphicsObjects<Molecule>();
+    deleteAllGraphicsObjects<Outline>();
+    deleteAllGraphicsObjects<Parallelepiped>();
+    deleteAllGraphicsObjects<PolyData>();
+    deleteAllGraphicsObjects<Polygon>();
+    deleteAllGraphicsObjects<PseudoColor>();
+    deleteAllGraphicsObjects<Sphere>();
+    deleteAllGraphicsObjects<Streamlines>();
+    deleteAllGraphicsObjects<Text3D>();
+    deleteAllGraphicsObjects<Volume>();
+    deleteAllGraphicsObjects<Warp>();
+
+    TRACE("Deleting ColorMaps");
+    // Delete color maps and data sets last in case references still
+    // exist
+    for (ColorMapHashmap::iterator itr = _colorMaps.begin();
+         itr != _colorMaps.end(); ++itr) {
+        delete itr->second;
+    }
+    _colorMaps.clear();
+    TRACE("Deleting DataSets");
+    for (DataSetHashmap::iterator itr = _dataSets.begin();
+         itr != _dataSets.end(); ++itr) {
+        delete itr->second;
+    }
+    _dataSets.clear();
+
+    clearFieldRanges();
+    clearUserFieldRanges();
+
+    TRACE("Leave");
+}
+
+/**
+ * \brief Add a DataSet to this Renderer
+ *
+ * This just adds the DataSet to the Renderer's list of data sets.
+ * In order to render the data, a graphics object using the data
+ * set must be added to the Renderer.
+ */
+void Renderer::addDataSet(const DataSetId& id)
+{
+    if (getDataSet(id) != NULL) {
+        WARN("Replacing existing DataSet %s", id.c_str());
+        deleteDataSet(id);
+    }
+    _dataSets[id] = new DataSet(id);
+}
+
+/**
+ * \brief Remove the specified DataSet and associated rendering objects
+ *
+ * The underlying DataSet and any associated graphics 
+ * objects are deleted, freeing the memory used.
+ */
+void Renderer::deleteDataSet(const DataSetId& id)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+        doAll = true;
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return;
+    }
+
+    do {
+        TRACE("Deleting dataset %s", itr->second->getName().c_str());
+
+        deleteGraphicsObject<Contour2D>(itr->second->getName());
+        deleteGraphicsObject<Contour3D>(itr->second->getName());
+        deleteGraphicsObject<Cutplane>(itr->second->getName());
+        deleteGraphicsObject<Glyphs>(itr->second->getName());
+        deleteGraphicsObject<HeightMap>(itr->second->getName());
+        deleteGraphicsObject<Image>(itr->second->getName());
+        deleteGraphicsObject<ImageCutplane>(itr->second->getName());
+        deleteGraphicsObject<LIC>(itr->second->getName());
+        deleteGraphicsObject<Molecule>(itr->second->getName());
+        deleteGraphicsObject<Outline>(itr->second->getName());
+        deleteGraphicsObject<PolyData>(itr->second->getName());
+        deleteGraphicsObject<PseudoColor>(itr->second->getName());
+        deleteGraphicsObject<Streamlines>(itr->second->getName());
+        deleteGraphicsObject<Volume>(itr->second->getName());
+        deleteGraphicsObject<Warp>(itr->second->getName());
+
+        TRACE("After deleting graphics objects");
+
+        delete itr->second;
+        itr = _dataSets.erase(itr);
+    } while (doAll && itr != _dataSets.end());
+
+    // Update cumulative data range
+    initFieldRanges();
+    updateFieldRanges();
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Get a list of DataSets this Renderer knows about
+ */
+void Renderer::getDataSetNames(std::vector<std::string>& names)
+{
+    names.clear();
+    for (DataSetHashmap::iterator itr = _dataSets.begin();
+         itr != _dataSets.end(); ++itr) {
+        names.push_back(itr->second->getName());
+    }
+}
+
+/**
+ * \brief Find the DataSet for the given DataSetId key
+ *
+ * \return A pointer to the DataSet, or NULL if not found
+ */
+DataSet *Renderer::getDataSet(const DataSetId& id)
+{
+    DataSetHashmap::iterator itr = _dataSets.find(id);
+    if (itr == _dataSets.end()) {
+#ifdef DEBUG
+        TRACE("DataSet not found: %s", id.c_str());
+#endif
+        return NULL;
+    } else
+        return itr->second;
+}
+
+/**
+ * \brief (Re-)load the data for the specified DataSet key from a file
+ */
+bool Renderer::setDataFile(const DataSetId& id, const char *filename)
+{
+    DataSet *ds = getDataSet(id);
+    if (ds) {
+        bool ret = ds->setDataFile(filename);
+        initFieldRanges();
+        updateFieldRanges();
+        _needsRedraw = true;
+        return ret;
+    } else
+        return false;
+}
+
+/**
+ * \brief (Re-)load the data for the specified DataSet key from a memory buffer
+ */
+bool Renderer::setData(const DataSetId& id, char *data, int nbytes)
+{
+    DataSet *ds = getDataSet(id);
+    if (ds) {
+        bool ret = ds->setData(data, nbytes);
+        initFieldRanges();
+        updateFieldRanges();
+        _needsRedraw = true;
+        return ret;
+    } else
+        return false;
+}
+
+/**
+ * \brief Set the active scalar field array by name for a DataSet
+ */
+bool Renderer::setDataSetActiveScalars(const DataSetId& id, const char *scalarName)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+        doAll = true;
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("DataSet not found: %s", id.c_str());
+        return false;
+    }
+
+    bool ret = true;
+    bool needRangeUpdate = false;
+    do {
+        const char *name = itr->second->getActiveScalarsName();
+        if (name == NULL || (strcmp(name, scalarName) != 0)) {
+            if (!itr->second->setActiveScalars(scalarName)) {
+                ret = false;
+            } else {
+                needRangeUpdate = true;
+            }
+        }
+    } while (doAll && ++itr != _dataSets.end());
+
+    if (needRangeUpdate) {
+         updateFieldRanges();
+        _needsRedraw = true;
+    } 
+
+    return ret;
+}
+
+/**
+ * \brief Set the active vector field array by name for a DataSet
+ */
+bool Renderer::setDataSetActiveVectors(const DataSetId& id, const char *vectorName)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+        doAll = true;
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("DataSet not found: %s", id.c_str());
+        return false;
+    }
+
+    bool ret = true;
+    bool needRangeUpdate = false;
+    do {
+        const char *name = itr->second->getActiveVectorsName();
+        if (name == NULL || (strcmp(name, vectorName) != 0)) {
+            if (!itr->second->setActiveVectors(vectorName)) {
+                ret = false;
+            } else {
+                needRangeUpdate = true;
+            }
+        }
+    } while (doAll && ++itr != _dataSets.end());
+
+    if (needRangeUpdate) {
+        updateFieldRanges();
+        _needsRedraw = true;
+    } 
+
+    return ret;
+}
+
+/**
+ * \brief Control whether the cumulative data range of datasets is used for 
+ * colormapping and contours
+ *
+ * NOTE: If using explicit range(s), setting cumulative ranges on will
+ * remove explicit ranges on all fields
+ */
+void Renderer::setUseCumulativeDataRange(bool state, bool onlyVisible)
+{
+    TRACE("use cumulative: %d, only visible: %d", (state ? 1 : 0), (onlyVisible ? 1 : 0));
+    _useCumulativeRange = state;
+    _cumulativeRangeOnlyVisible = onlyVisible;
+    clearUserFieldRanges();
+    updateFieldRanges();
+    resetAxes();
+    _needsRedraw = true;
+}
+
+void Renderer::resetAxes(double bounds[6])
+{
+    TRACE("Resetting axes");
+
+    if (_cubeAxesActor == NULL) {
+        initAxes();
+    }
+
+    if (_cameraMode == IMAGE) {
+        _cubeAxesActor->SetUse2DMode(1);
+    } else {
+        _cubeAxesActor->SetUse2DMode(0);
+    }
+
+    if (_cameraMode == IMAGE) {
+        return;
+    }
+
+    setAxesBounds(bounds);
+    setAxesRanges();
+}
+
+/**
+ * \brief Explicitly set world coordinate bounds of axes
+ *
+ * This determines the region in world coordinates around which
+ * the axes are drawn
+ */
+void Renderer::setAxesBounds(double min, double max)
+{
+    setAxisBounds(X_AXIS, min, max);
+    setAxisBounds(Y_AXIS, min, max);
+    setAxisBounds(Z_AXIS, min, max);
+}
+
+/**
+ * \brief Set axes bounds based on auto/explicit settings
+ */
+void Renderer::setAxesBounds(double boundsIn[6])
+{
+    double newBounds[6];
+    if (boundsIn != NULL) { 
+        memcpy(&newBounds[0], &boundsIn[0], sizeof(double)*6);
+    } else {
+        if (_axesAutoBounds[X_AXIS] ||
+            _axesAutoBounds[Y_AXIS] ||
+            _axesAutoBounds[Z_AXIS]) {
+            collectBounds(newBounds, _computeOnlyVisibleBounds);
+        }
+    }
+
+    double bounds[6];
+    if (_axesAutoBounds[X_AXIS]) {
+        memcpy(&bounds[0], &newBounds[0], sizeof(double)*2);
+    } else {
+        memcpy(&bounds[0], &_axesUserBounds[0], sizeof(double)*2);
+    }
+    if (_axesAutoBounds[Y_AXIS]) {
+        memcpy(&bounds[2], &newBounds[2], sizeof(double)*2);
+    } else {
+        memcpy(&bounds[2], &_axesUserBounds[2], sizeof(double)*2);
+    }
+    if (_axesAutoBounds[Z_AXIS]) {
+        memcpy(&bounds[4], &newBounds[4], sizeof(double)*2);
+    } else {
+        memcpy(&bounds[4], &_axesUserBounds[4], sizeof(double)*2);
+    }
+
+    _cubeAxesActor->SetBounds(bounds);
+
+    TRACE("Axis bounds: %g %g %g %g %g %g",
+              bounds[0],
+              bounds[1],
+              bounds[2],
+              bounds[3],
+              bounds[4],
+              bounds[5]);
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Toggle automatic vs. explicit range setting on all axes
+ */
+void Renderer::setAxesAutoBounds(bool state)
+{
+    _axesAutoBounds[X_AXIS] = state;
+    _axesAutoBounds[Y_AXIS] = state;
+    _axesAutoBounds[Z_AXIS] = state;
+
+    TRACE("Set axes bounds to %s", (state ? "auto" : "explicit"));
+
+    setAxesBounds();
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Toggle automatic vs. explicit range setting on all axes
+ */
+void Renderer::setAxisAutoBounds(Axis axis, bool state)
+{
+    _axesAutoBounds[axis] = state;
+
+    TRACE("Set axis %d bounds to %s", axis, (state ? "auto" : "explicit"));
+
+    setAxesBounds();
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Toggle automatic vs. explicit range setting on all axes
+ */
+void Renderer::setAxesAutoRange(bool state)
+{
+    _axesRangeMode[X_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
+    _axesRangeMode[Y_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
+    _axesRangeMode[Z_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
+
+    TRACE("Set axes range to %s", (state ? "auto" : "explicit"));
+
+    setAxesRanges();
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Explicitly set world coordinate bounds of axis
+ *
+ * This determines the region in world coordinates around which
+ * the axes are drawn
+ */
+void Renderer::setAxisBounds(Axis axis, double min, double max)
+{
+    double bounds[6];
+    _cubeAxesActor->GetBounds(bounds);
+
+    bounds[axis*2  ] = _axesUserBounds[axis*2  ] = min;
+    bounds[axis*2+1] = _axesUserBounds[axis*2+1] = max;
+
+    _cubeAxesActor->SetBounds(bounds);
+
+    _axesAutoBounds[axis] = false;
+
+    TRACE("Axis bounds: %g %g %g %g %g %g",
+              bounds[0],
+              bounds[1],
+              bounds[2],
+              bounds[3],
+              bounds[4],
+              bounds[5]);
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Toggle automatic vs. explicit range setting on specific axis
+ */
+void Renderer::setAxisAutoRange(Axis axis, bool state)
+{
+    _axesRangeMode[axis] = state ? RANGE_AUTO : RANGE_EXPLICIT;
+
+    TRACE("Set axis %d range to %s", axis, (state ? "auto" : "explicit"));
+
+    setAxesRanges();
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set explicit range on axes and disable auto range
+ */
+void Renderer::setAxesRange(double min, double max)
+{
+    setAxisRange(X_AXIS, min, max);
+    setAxisRange(Y_AXIS, min, max);
+    setAxisRange(Z_AXIS, min, max);
+}
+
+/**
+ * \brief Explicitly set power multiplier for axes labels
+ *
+ * The exponent will appear in the axis title and be omitted from labels
+ */
+void Renderer::setAxesLabelPowerScaling(int xPow, int yPow, int zPow, bool useCustom)
+{
+    _cubeAxesActor->SetLabelScaling(!useCustom, xPow, yPow, zPow);
+
+    TRACE("Set axis label scaling: custom: %d, powers: %d,%d,%d", (int)useCustom, xPow, yPow, zPow);
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set explicit range on axis and disable auto range
+ */
+void Renderer::setAxisRange(Axis axis, double min, double max)
+{
+    switch (axis) {
+    case X_AXIS:
+        _axesRangeMode[X_AXIS] = RANGE_EXPLICIT;
+        _cubeAxesActor->SetXAxisRange(min, max);
+        break;
+    case Y_AXIS:
+        _axesRangeMode[Y_AXIS] = RANGE_EXPLICIT;
+        _cubeAxesActor->SetYAxisRange(min, max);
+        break;
+    case Z_AXIS:
+        _axesRangeMode[Z_AXIS] = RANGE_EXPLICIT;
+        _cubeAxesActor->SetZAxisRange(min, max);
+        break;
+    default:
+        break;
+    }
+
+    TRACE("Set axis %d range to: %g, %g", axis, min, max);
+
+     _needsRedraw = true;
+}
+
+/**
+ * \brief Set axes ranges based on bounds and range modes
+ */
+void Renderer::setAxesRanges()
+{
+    computeAxesScale();
+
+    double bounds[6];
+    _cubeAxesActor->GetBounds(bounds);
+
+    double ranges[6];
+    if (_axesRangeMode[X_AXIS] != RANGE_EXPLICIT) {
+        ranges[0] = bounds[0] * _axesScale[X_AXIS];
+        ranges[1] = bounds[1] * _axesScale[X_AXIS];
+        _cubeAxesActor->SetXAxisRange(&ranges[0]);
+    } else {
+        _cubeAxesActor->GetXAxisRange(&ranges[0]);
+    }
+
+    if (_axesRangeMode[Y_AXIS] != RANGE_EXPLICIT) {
+        ranges[2] = bounds[2] * _axesScale[Y_AXIS];
+        ranges[3] = bounds[3] * _axesScale[Y_AXIS];
+        _cubeAxesActor->SetYAxisRange(&ranges[2]);
+    } else {
+        _cubeAxesActor->GetYAxisRange(&ranges[2]);
+    }
+
+    if (_axesRangeMode[Z_AXIS] != RANGE_EXPLICIT) {
+        ranges[4] = bounds[4] * _axesScale[Z_AXIS];
+        ranges[5] = bounds[5] * _axesScale[Z_AXIS];
+        _cubeAxesActor->SetZAxisRange(&ranges[4]);
+    } else {
+        _cubeAxesActor->GetZAxisRange(&ranges[4]);
+    }
+
+    TRACE("Axis ranges: %g %g %g %g %g %g",
+          ranges[0],
+          ranges[1],
+          ranges[2],
+          ranges[3],
+          ranges[4],
+          ranges[5]);
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Compute scaling from axes bounds to ranges
+ *
+ * Uses actor scaling to determine if world coordinates
+ * need to be scaled to undo actor scaling in range labels
+ */
+void Renderer::computeAxesScale()
+{
+    if (_axesRangeMode[X_AXIS] != RANGE_AUTO &&
+        _axesRangeMode[Y_AXIS] != RANGE_AUTO &&
+        _axesRangeMode[Z_AXIS] != RANGE_AUTO)
+        return;
+
+    double bounds[6];
+    if (_cameraMode == IMAGE) {
+        collectBounds(bounds, _computeOnlyVisibleBounds);
+    } else
+        _cubeAxesActor->GetBounds(bounds);
+
+    double unscaledBounds[6];
+    collectUnscaledBounds(unscaledBounds, _computeOnlyVisibleBounds);
+
+    if (_axesRangeMode[X_AXIS] == RANGE_AUTO) {
+        double sx = (bounds[1] == bounds[0]) ? 1.0 : (unscaledBounds[1] - unscaledBounds[0]) / (bounds[1] - bounds[0]);
+        _axesScale[X_AXIS] = sx;
+        TRACE("Setting X axis scale to: %g", sx);
+    }
+    if (_axesRangeMode[Y_AXIS] == RANGE_AUTO) {
+        double sy = (bounds[3] == bounds[2]) ? 1.0 : (unscaledBounds[3] - unscaledBounds[2]) / (bounds[3] - bounds[2]);
+        _axesScale[Y_AXIS] = sy;
+        TRACE("Setting Y axis scale to: %g", sy);
+    }
+    if (_axesRangeMode[Y_AXIS] == RANGE_AUTO) {
+        double sz = (bounds[5] == bounds[4]) ? 1.0 : (unscaledBounds[5] - unscaledBounds[4]) / (bounds[5] - bounds[4]);
+        _axesScale[Z_AXIS] = sz;
+        TRACE("Setting Z axis scale to: %g", sz);
+    }
+}
+
+/**
+ * \brief Set scaling factor to convert world coordinates to axis 
+ * label values in X, Y, Z
+ */
+void Renderer::setAxesScale(double scale)
+{
+    _axesRangeMode[X_AXIS] = RANGE_SCALE_BOUNDS;
+    _axesRangeMode[Y_AXIS] = RANGE_SCALE_BOUNDS;
+    _axesRangeMode[Z_AXIS] = RANGE_SCALE_BOUNDS;
+
+    _axesScale[X_AXIS] = scale;
+    _axesScale[Y_AXIS] = scale;
+    _axesScale[Z_AXIS] = scale;
+
+    TRACE("Setting axes scale to: %g", scale);
+
+    setAxesRanges();
+}
+
+/**
+ * \brief Set scaling factor to convert world coordinates to axis label values
+ */
+void Renderer::setAxisScale(Axis axis, double scale)
+{
+    _axesRangeMode[axis] = RANGE_SCALE_BOUNDS;
+    _axesScale[axis] = scale;
+
+    TRACE("Setting axis %d scale to: %g", axis, scale);
+
+    setAxesRanges();
+}
+
+/**
+ * \brief Set an origin point within the axes bounds where the axes will intersect
+ *
+ * \param x X coordinate of origin
+ * \param y Y coordinate of origin
+ * \param z Z coordinate of origin
+ * \param useCustom Flag indicating if custom origin is to be used/enabled
+ */
+void Renderer::setAxesOrigin(double x, double y, double z, bool useCustom)
+{
+    _cubeAxesActor->SetAxisOrigin(x, y, z);
+    _cubeAxesActor->SetUseAxisOrigin((useCustom ? 1 : 0));
+
+    _needsRedraw = true;
+}
+
+
+void Renderer::initOrientationMarkers()
+{
+    if (_axesActor == NULL) {
+        _axesActor = vtkSmartPointer<vtkAxesActor>::New();
+    }
+    if (_annotatedCubeActor == NULL) {
+        _annotatedCubeActor = vtkSmartPointer<vtkAnnotatedCubeActor>::New();
+    }
+    _renderer->AddViewProp(_axesActor);
+}
+
+/**
+ * \brief Set inital properties on the 2D Axes
+ */
+void Renderer::initAxes()
+{
+    TRACE("Initializing axes");
+#ifdef USE_CUSTOM_AXES
+    if (_cubeAxesActor == NULL)
+        _cubeAxesActor = vtkSmartPointer<vtkRpCubeAxesActor>::New();
+#else
+    if (_cubeAxesActor == NULL)
+        _cubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
+#endif
+    _cubeAxesActor->SetCamera(_renderer->GetActiveCamera());
+    _cubeAxesActor->GetProperty()->LightingOff();
+    // Don't offset labels at origin
+    _cubeAxesActor->SetCornerOffset(0);
+    _cubeAxesActor->SetFlyModeToStaticTriad();
+
+    _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
+    _cubeAxesActor->SetDrawXInnerGridlines(0);
+    _cubeAxesActor->SetDrawYInnerGridlines(0);
+    _cubeAxesActor->SetDrawZInnerGridlines(0);
+    _cubeAxesActor->SetDrawXGridpolys(0);
+    _cubeAxesActor->SetDrawYGridpolys(0);
+    _cubeAxesActor->SetDrawZGridpolys(0);
+    _cubeAxesActor->SetDistanceLODThreshold(1);
+    _cubeAxesActor->SetEnableDistanceLOD(0);
+    _cubeAxesActor->SetViewAngleLODThreshold(0);
+    _cubeAxesActor->SetEnableViewAngleLOD(0);
+    // Attempt to set font sizes for 2D (this currently doesn't work)
+    for (int i = 0; i < 3; i++) {
+        _cubeAxesActor->GetTitleTextProperty(i)->SetFontSize(14);
+        _cubeAxesActor->GetLabelTextProperty(i)->SetFontSize(12);
+    }
+    // Set font pixel size for 3D
+    _cubeAxesActor->SetScreenSize(8.0);
+
+    _cubeAxesActor->SetBounds(0, 1, 0, 1, 0, 1);
+    _cubeAxesActor->SetXAxisRange(0, 1);
+    _cubeAxesActor->SetYAxisRange(0, 1);
+    _cubeAxesActor->SetZAxisRange(0, 1);
+    for (int axis = 0; axis < 3; axis++) {
+        _axesAutoBounds[axis] = true;
+        _axesRangeMode[axis] = RANGE_AUTO;
+        _axesScale[axis] = 1.0;
+    }
+
+    double axesColor[] = {1,1,1};
+    setAxesColor(axesColor);
+
+    if (!_renderer->HasViewProp(_cubeAxesActor))
+        _renderer->AddViewProp(_cubeAxesActor);
+
+    if (_cameraMode == IMAGE) {
+        _cubeAxesActor->SetUse2DMode(1);
+    } else {
+        _cubeAxesActor->SetUse2DMode(0);
+    }
+}
+
+/**
+ * \brief Set Fly mode of axes
+ */
+void Renderer::setAxesFlyMode(AxesFlyMode mode)
+{
+    if (_cubeAxesActor == NULL)
+        return;
+
+    switch (mode) {
+    case FLY_STATIC_EDGES:
+        _cubeAxesActor->SetFlyModeToStaticEdges();
+        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
+        break;
+    case FLY_STATIC_TRIAD:
+        _cubeAxesActor->SetFlyModeToStaticTriad();
+        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
+        break;
+    case FLY_OUTER_EDGES:
+        _cubeAxesActor->SetFlyModeToOuterEdges();
+        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
+        break;
+    case FLY_FURTHEST_TRIAD:
+        _cubeAxesActor->SetFlyModeToFurthestTriad();
+        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_FURTHEST);
+        break;
+    case FLY_CLOSEST_TRIAD:
+    default:
+        _cubeAxesActor->SetFlyModeToClosestTriad();
+        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_CLOSEST);
+        break;
+    }
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set color of axes, ticks, labels, titles
+ */
+void Renderer::setAxesColor(double color[3], double opacity)
+{
+    setAxesTitleColor(color, opacity);
+    setAxesLabelColor(color, opacity);
+    setAxesLinesColor(color, opacity);
+    setAxesGridlinesColor(color, opacity);
+    setAxesInnerGridlinesColor(color, opacity);
+    setAxesGridpolysColor(color, opacity);
+}
+
+/**
+ * \brief Set color of axes title text
+ */
+void Renderer::setAxesTitleColor(double color[3], double opacity)
+{
+    setAxisTitleColor(X_AXIS, color, opacity);
+    setAxisTitleColor(Y_AXIS, color, opacity);
+    setAxisTitleColor(Z_AXIS, color, opacity);
+}
+
+/**
+ * \brief Set color of axes label text
+ */
+void Renderer::setAxesLabelColor(double color[3], double opacity)
+{
+    setAxisLabelColor(X_AXIS, color, opacity);
+    setAxisLabelColor(Y_AXIS, color, opacity);
+    setAxisLabelColor(Z_AXIS, color, opacity);
+}
+
+/**
+ * \brief Set color of main axes lines
+ */
+void Renderer::setAxesLinesColor(double color[3], double opacity)
+{
+    setAxisLinesColor(X_AXIS, color, opacity);
+    setAxisLinesColor(Y_AXIS, color, opacity);
+    setAxisLinesColor(Z_AXIS, color, opacity);
+}
+
+/**
+ * \brief Set color of axis gridlines
+ */
+void Renderer::setAxesGridlinesColor(double color[3], double opacity)
+{
+    setAxisGridlinesColor(X_AXIS, color, opacity);
+    setAxisGridlinesColor(Y_AXIS, color, opacity);
+    setAxisGridlinesColor(Z_AXIS, color, opacity);
+}
+
+/**
+ * \brief Set color of axes inner gridlines
+ */
+void Renderer::setAxesInnerGridlinesColor(double color[3], double opacity)
+{
+    setAxisInnerGridlinesColor(X_AXIS, color, opacity);
+    setAxisInnerGridlinesColor(Y_AXIS, color, opacity);
+    setAxisInnerGridlinesColor(Z_AXIS, color, opacity);
+}
+
+/**
+ * \brief Set color of axes inner grid polygons
+ */
+void Renderer::setAxesGridpolysColor(double color[3], double opacity)
+{
+    setAxisGridpolysColor(X_AXIS, color, opacity);
+    setAxisGridpolysColor(Y_AXIS, color, opacity);
+    setAxisGridpolysColor(Z_AXIS, color, opacity);
+}
+
+/**
+ * \brief Turn on/off rendering of all enabled axes
+ */
+void Renderer::setAxesVisibility(bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        _cubeAxesActor->SetVisibility((state ? 1 : 0));
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of all axes gridlines
+ */
+void Renderer::setAxesGridVisibility(bool state)
+{
+    setAxisGridVisibility(X_AXIS, state);
+    setAxisGridVisibility(Y_AXIS, state);
+    setAxisGridVisibility(Z_AXIS, state);
+}
+
+/**
+ * \brief Turn on/off rendering of all axes inner gridlines
+ */
+void Renderer::setAxesInnerGridVisibility(bool state)
+{
+    setAxisInnerGridVisibility(X_AXIS, state);
+    setAxisInnerGridVisibility(Y_AXIS, state);
+    setAxisInnerGridVisibility(Z_AXIS, state);
+}
+
+/**
+ * \brief Turn on/off rendering of all axes inner grid polygons
+ */
+void Renderer::setAxesGridpolysVisibility(bool state)
+{
+    setAxisGridpolysVisibility(X_AXIS, state);
+    setAxisGridpolysVisibility(Y_AXIS, state);
+    setAxisGridpolysVisibility(Z_AXIS, state);
+}
+
+/**
+ * \brief Turn on/off rendering of all axis labels
+ */
+void Renderer::setAxesLabelVisibility(bool state)
+{
+    setAxisLabelVisibility(X_AXIS, state);
+    setAxisLabelVisibility(Y_AXIS, state);
+    setAxisLabelVisibility(Z_AXIS, state);
+}
+
+/**
+ * \brief Turn on/off rendering of all axis ticks
+ */
+void Renderer::setAxesTickVisibility(bool state)
+{
+    setAxisTickVisibility(X_AXIS, state);
+    setAxisTickVisibility(Y_AXIS, state);
+    setAxisTickVisibility(Z_AXIS, state);
+}
+
+/**
+ * \brief Turn on/off rendering of all axis ticks
+ */
+void Renderer::setAxesMinorTickVisibility(bool state)
+{
+    setAxisMinorTickVisibility(X_AXIS, state);
+    setAxisMinorTickVisibility(Y_AXIS, state);
+    setAxisMinorTickVisibility(Z_AXIS, state);
+}
+
+/**
+ * \brief Control position of ticks on 3D axes
+ */
+void Renderer::setAxesTickPosition(AxesTickPosition pos)
+{
+    if (_cubeAxesActor == NULL)
+        return;
+
+    switch (pos) {
+    case TICKS_BOTH:
+        _cubeAxesActor->SetTickLocationToBoth();
+        break;
+    case TICKS_OUTSIDE:
+        _cubeAxesActor->SetTickLocationToOutside();
+        break;
+    case TICKS_INSIDE:
+    default:
+        _cubeAxesActor->SetTickLocationToInside();
+        break;
+    }
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Controls label scaling by power of 10
+ */
+void Renderer::setAxesLabelScaling(bool autoScale, int xpow, int ypow, int zpow)
+{
+    if (_cubeAxesActor != NULL) {
+        _cubeAxesActor->SetLabelScaling(autoScale, xpow, ypow, zpow);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set axes title/label font size in pixels
+ */
+void Renderer::setAxesPixelFontSize(double screenSize)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axes font size to: %g px", screenSize);
+        _cubeAxesActor->SetScreenSize(screenSize);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set axis title font family
+ */
+void Renderer::setAxesTitleFont(const char *fontName)
+{
+    setAxisTitleFont(X_AXIS, fontName);
+    setAxisTitleFont(Y_AXIS, fontName);
+    setAxisTitleFont(Z_AXIS, fontName);
+}
+
+/**
+ * \brief Set axis title font size
+ */
+void Renderer::setAxesTitleFontSize(int sz)
+{
+    setAxisTitleFontSize(X_AXIS, sz);
+    setAxisTitleFontSize(Y_AXIS, sz);
+    setAxisTitleFontSize(Z_AXIS, sz);
+}
+
+/**
+ * \brief Set orientation (as rotation in degrees) of axis title
+ */
+void Renderer::setAxesTitleOrientation(double orientation)
+{
+    setAxisTitleOrientation(X_AXIS, orientation);
+    setAxisTitleOrientation(Y_AXIS, orientation);
+    setAxisTitleOrientation(Z_AXIS, orientation);
+}
+
+/**
+ * \brief Set axis label font family
+ */
+void Renderer::setAxesLabelFont(const char *fontName)
+{
+    setAxisLabelFont(X_AXIS, fontName);
+    setAxisLabelFont(Y_AXIS, fontName);
+    setAxisLabelFont(Z_AXIS, fontName);
+}
+
+/**
+ * \brief Set axis label font size
+ */
+void Renderer::setAxesLabelFontSize(int sz)
+{
+    setAxisLabelFontSize(X_AXIS, sz);
+    setAxisLabelFontSize(Y_AXIS, sz);
+    setAxisLabelFontSize(Z_AXIS, sz);
+}
+
+/**
+ * \brief Set orientation (as rotation in degrees) of axis labels
+ */
+void Renderer::setAxesLabelOrientation(double orientation)
+{
+    setAxisLabelOrientation(X_AXIS, orientation);
+    setAxisLabelOrientation(Y_AXIS, orientation);
+    setAxisLabelOrientation(Z_AXIS, orientation);
+}
+
+/**
+ * \brief Set printf style label format string
+ */
+void Renderer::setAxesLabelFormat(const char *format)
+{
+    setAxisLabelFormat(X_AXIS, format);
+    setAxisLabelFormat(Y_AXIS, format);
+    setAxisLabelFormat(Z_AXIS, format);
+}
+
+/**
+ * \brief Turn on/off rendering of the specified axis
+ */
+void Renderer::setAxisVisibility(Axis axis, bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetXAxisVisibility((state ? 1 : 0));
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetYAxisVisibility((state ? 1 : 0));
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetZAxisVisibility((state ? 1 : 0));
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of single axis gridlines
+ */
+void Renderer::setAxisGridVisibility(Axis axis, bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetDrawXGridlines((state ? 1 : 0));
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetDrawYGridlines((state ? 1 : 0));
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetDrawZGridlines((state ? 1 : 0));
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of single axis gridlines
+ */
+void Renderer::setAxisInnerGridVisibility(Axis axis, bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetDrawXInnerGridlines((state ? 1 : 0));
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetDrawYInnerGridlines((state ? 1 : 0));
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetDrawZInnerGridlines((state ? 1 : 0));
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of single axis gridlines
+ */
+void Renderer::setAxisGridpolysVisibility(Axis axis, bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetDrawXGridpolys((state ? 1 : 0));
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetDrawYGridpolys((state ? 1 : 0));
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetDrawZGridpolys((state ? 1 : 0));
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Toggle label visibility for the specified axis
+ */
+void Renderer::setAxisLabelVisibility(Axis axis, bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetXAxisLabelVisibility((state ? 1 : 0));
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetYAxisLabelVisibility((state ? 1 : 0));
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetZAxisLabelVisibility((state ? 1 : 0));
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Toggle tick visibility for the specified axis
+ */
+void Renderer::setAxisTickVisibility(Axis axis, bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetXAxisTickVisibility((state ? 1 : 0));
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetYAxisTickVisibility((state ? 1 : 0));
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetZAxisTickVisibility((state ? 1 : 0));
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Toggle tick visibility for the specified axis
+ */
+void Renderer::setAxisMinorTickVisibility(Axis axis, bool state)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetXAxisMinorTickVisibility((state ? 1 : 0));
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetYAxisMinorTickVisibility((state ? 1 : 0));
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetZAxisMinorTickVisibility((state ? 1 : 0));
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set color of axis lines, ticks, labels, titles
+ */
+void Renderer::setAxisColor(Axis axis, double color[3], double opacity)
+{
+    setAxisTitleColor(axis, color, opacity);
+    setAxisLabelColor(axis, color, opacity);
+    setAxisLinesColor(axis, color, opacity);
+    setAxisGridlinesColor(axis, color, opacity);
+    setAxisInnerGridlinesColor(axis, color, opacity);
+    setAxisGridpolysColor(axis, color, opacity);
+}
+
+/**
+ * \brief Set color of axis title text
+ */
+void Renderer::setAxisTitleColor(Axis axis, double color[3], double opacity)
+{
+    if (_cubeAxesActor != NULL) {
+        _cubeAxesActor->GetTitleTextProperty(axis)->SetColor(color);
+        _cubeAxesActor->GetTitleTextProperty(axis)->SetOpacity(opacity);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set color of axis label text
+ */
+void Renderer::setAxisLabelColor(Axis axis, double color[3], double opacity)
+{
+    if (_cubeAxesActor != NULL) {
+        _cubeAxesActor->GetLabelTextProperty(axis)->SetColor(color);
+        _cubeAxesActor->GetLabelTextProperty(axis)->SetOpacity(opacity);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set color of main axis line
+ */
+void Renderer::setAxisLinesColor(Axis axis, double color[3], double opacity)
+{
+    if (_cubeAxesActor != NULL) {
+        switch (axis) {
+        case X_AXIS:
+            _cubeAxesActor->GetXAxesLinesProperty()->SetColor(color);
+            _cubeAxesActor->GetXAxesLinesProperty()->SetOpacity(opacity);
+            break;
+        case Y_AXIS:
+            _cubeAxesActor->GetYAxesLinesProperty()->SetColor(color);
+            _cubeAxesActor->GetYAxesLinesProperty()->SetOpacity(opacity);
+            break;
+        case Z_AXIS:
+            _cubeAxesActor->GetZAxesLinesProperty()->SetColor(color);
+            _cubeAxesActor->GetZAxesLinesProperty()->SetOpacity(opacity);
+            break;
+        default:
+            ;
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set color of axis gridlines
+ */
+void Renderer::setAxisGridlinesColor(Axis axis, double color[3], double opacity)
+{
+    if (_cubeAxesActor != NULL) {
+        switch (axis) {
+        case X_AXIS:
+            _cubeAxesActor->GetXAxesGridlinesProperty()->SetColor(color);
+            _cubeAxesActor->GetXAxesGridlinesProperty()->SetOpacity(opacity);
+            break;
+        case Y_AXIS:
+            _cubeAxesActor->GetYAxesGridlinesProperty()->SetColor(color);
+            _cubeAxesActor->GetYAxesGridlinesProperty()->SetOpacity(opacity);
+            break;
+        case Z_AXIS:
+            _cubeAxesActor->GetZAxesGridlinesProperty()->SetColor(color);
+            _cubeAxesActor->GetZAxesGridlinesProperty()->SetOpacity(opacity);
+            break;
+        default:
+            ;
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set color of axis inner gridlines
+ */
+void Renderer::setAxisInnerGridlinesColor(Axis axis, double color[3], double opacity)
+{
+    if (_cubeAxesActor != NULL) {
+        switch (axis) {
+        case X_AXIS:
+            _cubeAxesActor->GetXAxesInnerGridlinesProperty()->SetColor(color);
+            _cubeAxesActor->GetXAxesInnerGridlinesProperty()->SetOpacity(opacity);
+            break;
+        case Y_AXIS:
+            _cubeAxesActor->GetYAxesInnerGridlinesProperty()->SetColor(color);
+            _cubeAxesActor->GetYAxesInnerGridlinesProperty()->SetOpacity(opacity);
+            break;
+        case Z_AXIS:
+            _cubeAxesActor->GetZAxesInnerGridlinesProperty()->SetColor(color);
+            _cubeAxesActor->GetZAxesInnerGridlinesProperty()->SetOpacity(opacity);
+            break;
+        default:
+            ;
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set color of axis inner grid polygons
+ *
+ * Default opacity is 0.6
+ */
+void Renderer::setAxisGridpolysColor(Axis axis, double color[3], double opacity)
+{
+    if (_cubeAxesActor != NULL) {
+        switch (axis) {
+        case X_AXIS:
+            _cubeAxesActor->GetXAxesGridpolysProperty()->SetColor(color);
+            _cubeAxesActor->GetXAxesGridpolysProperty()->SetOpacity(opacity);
+            break;
+        case Y_AXIS:
+            _cubeAxesActor->GetYAxesGridpolysProperty()->SetColor(color);
+            _cubeAxesActor->GetYAxesGridpolysProperty()->SetOpacity(opacity);
+            break;
+        case Z_AXIS:
+            _cubeAxesActor->GetZAxesGridpolysProperty()->SetColor(color);
+            _cubeAxesActor->GetZAxesGridpolysProperty()->SetOpacity(opacity);
+            break;
+        default:
+            ;
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set title of the specified axis
+ */
+void Renderer::setAxisTitle(Axis axis, const char *title)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetXTitle(title);
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetYTitle(title);
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetZTitle(title);
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set units of the specified axis
+ */
+void Renderer::setAxisUnits(Axis axis, const char *units)
+{
+    if (_cubeAxesActor != NULL) {
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetXUnits(units);
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetYUnits(units);
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetZUnits(units);
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set axis title font family
+ */
+void Renderer::setAxisTitleFont(Axis axis, const char *fontName)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axis %d title font to: '%s'", axis, fontName);
+        _cubeAxesActor->GetTitleTextProperty(axis)->SetFontFamilyAsString(fontName);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set axis title font size (and optionally pixel size)
+ */
+void Renderer::setAxisTitleFontSize(Axis axis, int sz)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axis %d title font size to: %d", axis, sz);
+        _cubeAxesActor->GetTitleTextProperty(axis)->SetFontSize(sz);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set orientation (as rotation in degrees) of axis titles
+ */
+void Renderer::setAxisTitleOrientation(Axis axis, double orientation)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axis %d title orientation to: %g", axis, orientation);
+        _cubeAxesActor->GetTitleTextProperty(axis)->SetOrientation(orientation);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set axis label font family
+ */
+void Renderer::setAxisLabelFont(Axis axis, const char *fontName)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axis %d label font to: '%s'", axis, fontName);
+        _cubeAxesActor->GetLabelTextProperty(axis)->SetFontFamilyAsString(fontName);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set axis label font size (and optionally pixel size)
+ */
+void Renderer::setAxisLabelFontSize(Axis axis, int sz)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axis %d label font size to: %d", axis, sz);
+        _cubeAxesActor->GetLabelTextProperty(axis)->SetFontSize(sz);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set orientation (as rotation in degrees) of axis labels
+ */
+void Renderer::setAxisLabelOrientation(Axis axis, double orientation)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axis %d label orientation to: %g", axis, orientation);
+        _cubeAxesActor->GetLabelTextProperty(axis)->SetOrientation(orientation);
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Set printf style label format string
+ */
+void Renderer::setAxisLabelFormat(Axis axis, const char *format)
+{
+    if (_cubeAxesActor != NULL) {
+        TRACE("Setting axis %d label format to: '%s'", axis, format);
+        if (axis == X_AXIS) {
+            _cubeAxesActor->SetXLabelFormat(format);
+#ifdef USE_CUSTOM_AXES
+            _cubeAxesActor->XAutoLabelFormatOff();
+#endif
+        } else if (axis == Y_AXIS) {
+            _cubeAxesActor->SetYLabelFormat(format);
+#ifdef USE_CUSTOM_AXES
+            _cubeAxesActor->YAutoLabelFormatOff();
+#endif
+        } else if (axis == Z_AXIS) {
+            _cubeAxesActor->SetZLabelFormat(format);
+#ifdef USE_CUSTOM_AXES
+            _cubeAxesActor->ZAutoLabelFormatOff();
+#endif
+        }
+        _needsRedraw = true;
+    }
+}
+
+/**
+ * \brief Notify graphics objects that color map has changed
+ */
+void Renderer::updateColorMap(ColorMap *cmap)
+{
+    TRACE("%s", cmap->getName().c_str());
+    updateGraphicsObjectColorMap<Contour2D>(cmap);
+    updateGraphicsObjectColorMap<Contour3D>(cmap);
+    updateGraphicsObjectColorMap<Cutplane>(cmap);
+    updateGraphicsObjectColorMap<Glyphs>(cmap);
+    updateGraphicsObjectColorMap<HeightMap>(cmap);
+    updateGraphicsObjectColorMap<Image>(cmap);
+    updateGraphicsObjectColorMap<ImageCutplane>(cmap);
+    updateGraphicsObjectColorMap<LIC>(cmap);
+    updateGraphicsObjectColorMap<Molecule>(cmap);
+    updateGraphicsObjectColorMap<PolyData>(cmap);
+    updateGraphicsObjectColorMap<PseudoColor>(cmap);
+    updateGraphicsObjectColorMap<Streamlines>(cmap);
+    updateGraphicsObjectColorMap<Volume>(cmap);
+    updateGraphicsObjectColorMap<Warp>(cmap);
+    TRACE("Leave");
+}
+
+/**
+ * \brief Check if a ColorMap is in use by graphics objects
+ */
+bool Renderer::colorMapUsed(ColorMap *cmap)
+{
+    if (graphicsObjectColorMapUsed<Contour2D>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Contour3D>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Cutplane>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Glyphs>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<HeightMap>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Image>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<ImageCutplane>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<LIC>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Molecule>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<PolyData>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<PseudoColor>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Streamlines>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Volume>(cmap))
+        return true;
+    if (graphicsObjectColorMapUsed<Warp>(cmap))
+        return true;
+
+    return false;
+}
+
+/**
+ * \brief Add/replace a ColorMap for use in the Renderer
+ */
+void Renderer::addColorMap(const ColorMapId& id, ColorMap *colorMap)
+{
+    if (colorMap != NULL) {
+        colorMap->build();
+        if (getColorMap(id) != NULL) {
+            TRACE("Replacing existing ColorMap %s", id.c_str());
+            // Copy to current colormap to avoid invalidating 
+            // pointers in graphics objects using the color map
+            *_colorMaps[id] = *colorMap;
+            delete colorMap;
+            // Notify graphics objects of change
+            updateColorMap(_colorMaps[id]);
+        } else
+            _colorMaps[id] = colorMap;
+    } else {
+        ERROR("NULL ColorMap");
+    }
+}
+
+/**
+ * \brief Return the ColorMap associated with the colormap key given
+ */
+ColorMap *Renderer::getColorMap(const ColorMapId& id)
+{
+    ColorMapHashmap::iterator itr = _colorMaps.find(id);
+
+    if (itr == _colorMaps.end())
+        return NULL;
+    else
+        return itr->second;
+}
+
+/**
+ * \brief Remove the colormap associated with the key given
+ *
+ * The underlying vtkLookupTable will be deleted if it is not referenced
+ * by any other objects
+ */
+void Renderer::deleteColorMap(const ColorMapId& id)
+{
+    ColorMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _colorMaps.begin();
+        doAll = true;
+    } else {
+        itr = _colorMaps.find(id);
+    }
+
+    if (itr == _colorMaps.end()) {
+        ERROR("Unknown ColorMap %s", id.c_str());
+        return;
+    }
+
+    do {
+        if (itr->second->getName().compare("default") == 0 ||
+            itr->second->getName().compare("grayDefault") == 0 ||
+            itr->second->getName().compare("volumeDefault") == 0 ||
+            itr->second->getName().compare("elementDefault") == 0) {
+            if (id.compare("all") != 0) {
+                WARN("Cannot delete a default color map");
+            }
+            continue;
+        } else if (colorMapUsed(itr->second)) {
+            WARN("Cannot delete color map '%s', it is in use", itr->second->getName().c_str());
+            continue;
+        }
+
+        TRACE("Deleting ColorMap %s", itr->second->getName().c_str());
+
+        delete itr->second;
+        itr = _colorMaps.erase(itr);
+    } while (doAll && itr != _colorMaps.end());
+}
+
+/**
+ * \brief Set the number of discrete colors used in the colormap's lookup table
+ *
+ * Note that the number of table entries is independent of the number of 
+ * control points in the color/alpha ramp
+ */
+void Renderer::setColorMapNumberOfTableEntries(const ColorMapId& id, int numEntries)
+{
+    ColorMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _colorMaps.begin();
+        doAll = true;
+    } else {
+        itr = _colorMaps.find(id);
+    }
+
+    if (itr == _colorMaps.end()) {
+        ERROR("Unknown ColorMap %s", id.c_str());
+        return;
+    }
+
+    if (numEntries < 0) {
+        numEntries = 256;
+        TRACE("Setting numEntries to default value of %d", numEntries);
+    }
+
+    do {
+        if (itr->second->getName() == "elementDefault") {
+            TRACE("Can't change number of table entries for default element color map");
+        } else {
+            itr->second->setNumberOfTableEntries(numEntries);
+            updateColorMap(itr->second);
+        }
+    } while (doAll && ++itr != _colorMaps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Render a labelled legend image for the given colormap
+ *
+ * The field is assumed to be the active scalar or vector field
+ * based on the legendType.
+ *
+ * \param[in] id ColorMap name
+ * \param[in] dataSetID DataSet name
+ * \param[in] legendType scalar or vector field legend
+ * \param[in,out] title If supplied, draw title ("#auto" means to
+ * fill in field name and draw).  If blank, do not draw title.  
+ * If title was blank or "#auto", will be filled with field name on
+ * return
+ * \param[in,out] range Data range to use in legend.  Set min > max to have
+ * range computed, will be filled with valid min and max values
+ * \param[in] width Pixel width of legend (aspect controls orientation)
+ * \param[in] height Pixel height of legend (aspect controls orientation)
+ * \param[in] opaque Flag to control if legend is rendered opaque or translucent
+ * \param[in] numLabels Number of labels to render (includes min/max)
+ * \param[in,out] imgData Pointer to array to fill with image bytes. Array
+ * will be resized if needed.
+ * \return The image is rendered into the supplied array, false is 
+ * returned if the color map is not found
+ */
+bool Renderer::renderColorMap(const ColorMapId& id, 
+                              const DataSetId& dataSetID,
+                              Renderer::LegendType legendType,
+                              std::string& title,
+                              double range[2],
+                              int width, int height,
+                              bool opaque,
+                              int numLabels,
+                              vtkUnsignedCharArray *imgData)
+{
+    DataSet *dataSet = NULL;
+    if (dataSetID.compare("all") == 0) {
+        if (_dataSets.empty()) {
+            WARN("No DataSets exist, can't fill title or range");
+            return renderColorMap(id, dataSetID, legendType,
+                                  NULL,
+                                  DataSet::POINT_DATA,
+                                  title, range, width, height, opaque, numLabels, imgData);
+        } else {
+            dataSet = _dataSets.begin()->second;
+        }
+    } else {
+        dataSet = getDataSet(dataSetID);
+        if (dataSet == NULL) {
+            ERROR("DataSet '%s' not found", dataSetID.c_str());
+            return false;
+        }
+    }
+
+    if (legendType == LEGEND_SCALAR) {
+        return renderColorMap(id, dataSetID, legendType,
+                              dataSet->getActiveScalarsName(),
+                              dataSet->getActiveScalarsType(),
+                              title, range, width, height, opaque, numLabels, imgData);
+    } else {
+        return renderColorMap(id, dataSetID, legendType,
+                              dataSet->getActiveVectorsName(),
+                              dataSet->getActiveVectorsType(),
+                              title, range, width, height, opaque, numLabels, imgData);
+    }
+}
+
+/**
+ * \brief Render a labelled legend image for the given colormap
+ *
+ * The field is assumed to be point data, if the field is not found
+ * as point data, cell data is used.
+ *
+ * \param[in] id ColorMap name
+ * \param[in] dataSetID DataSet name
+ * \param[in] legendType scalar or vector field legend
+ * \param[in] fieldName Name of the field array this legend is for
+ * \param[in,out] title If supplied, draw title ("#auto" means to
+ * fill in field name and draw).  If blank, do not draw title.  
+ * If title was blank or "#auto", will be filled with field name on
+ * return
+ * \param[in,out] range Data range to use in legend.  Set min > max to have
+ * range computed, will be filled with valid min and max values
+ * \param[in] width Pixel width of legend (aspect controls orientation)
+ * \param[in] height Pixel height of legend (aspect controls orientation)
+ * \param[in] opaque Flag to control if legend is rendered opaque or translucent
+ * \param[in] numLabels Number of labels to render (includes min/max)
+ * \param[in,out] imgData Pointer to array to fill with image bytes. Array
+ * will be resized if needed.
+ * \return The image is rendered into the supplied array, false is 
+ * returned if the color map is not found
+ */
+bool Renderer::renderColorMap(const ColorMapId& id, 
+                              const DataSetId& dataSetID,
+                              Renderer::LegendType legendType,
+                              const char *fieldName,
+                              std::string& title,
+                              double range[2],
+                              int width, int height,
+                              bool opaque,
+                              int numLabels,
+                              vtkUnsignedCharArray *imgData)
+{
+    DataSet *dataSet = NULL;
+    if (dataSetID.compare("all") == 0) {
+        if (_dataSets.empty()) {
+            WARN("No DataSets exist, can't fill title or range");
+            return renderColorMap(id, dataSetID, legendType,
+                                  NULL,
+                                  DataSet::POINT_DATA,
+                                  title, range, width, height, opaque, numLabels, imgData);
+        } else {
+            dataSet = _dataSets.begin()->second;
+        }
+    } else {
+        dataSet = getDataSet(dataSetID);
+        if (dataSet == NULL) {
+            ERROR("DataSet '%s' not found", dataSetID.c_str());
+            return false;
+        }
+    }
+
+    DataSet::DataAttributeType attrType;
+    int numComponents;
+
+    dataSet->getFieldInfo(fieldName, &attrType, &numComponents);
+
+    return renderColorMap(id, dataSetID, legendType,
+                          fieldName,
+                          attrType,
+                          title, range, width, height, opaque, numLabels, imgData);
+}
+
+/**
+ * \brief Render a labelled legend image for the given colormap
+ *
+ * \param[in] id ColorMap name
+ * \param[in] dataSetID DataSet name
+ * \param[in] legendType scalar or vector field legend
+ * \param[in] fieldName Name of the field array this legend is for
+ * \param[in] type DataAttributeType of the field
+ * \param[in,out] title If supplied, draw title ("#auto" means to
+ * fill in field name and draw).  If blank, do not draw title.  
+ * If title was blank or "#auto", will be filled with field name on
+ * return
+ * \param[in,out] range Data range to use in legend.  Set min > max to have
+ * range computed, will be filled with valid min and max values
+ * \param[in] width Pixel width of legend (aspect controls orientation)
+ * \param[in] height Pixel height of legend (aspect controls orientation)
+ * \param[in] opaque Flag to control if legend is rendered opaque or translucent
+ * \param[in] numLabels Number of labels to render (includes min/max)
+ * \param[in,out] imgData Pointer to array to fill with image bytes. Array
+ * will be resized if needed.
+ * \return The image is rendered into the supplied array, false is 
+ * returned if the color map is not found
+ */
+bool Renderer::renderColorMap(const ColorMapId& id, 
+                              const DataSetId& dataSetID,
+                              Renderer::LegendType legendType,
+                              const char *fieldName,
+                              DataSet::DataAttributeType type,
+                              std::string& title,
+                              double range[2],
+                              int width, int height,
+                              bool opaque,
+                              int numLabels,
+                              vtkUnsignedCharArray *imgData)
+{
+    TRACE("Enter");
+    ColorMap *colorMap = getColorMap(id);
+    if (colorMap == NULL)
+        return false;
+#ifdef LEGEND_SOFTWARE_RENDER
+    ColorMap::renderColorMap(colorMap, width, height, imgData, opaque, _bgColor,
+#ifdef RENDER_TARGA
+                             true, TARGA_BYTES_PER_PIXEL
+#else
+                             false
+#endif
+                             );
+#else
+    if (_legendRenderWindow == NULL) {
+        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
+        _legendRenderWindow->SetMultiSamples(0);
+#ifdef USE_OFFSCREEN_RENDERING
+        _legendRenderWindow->DoubleBufferOff();
+        _legendRenderWindow->OffScreenRenderingOn();
+#else
+        _legendRenderWindow->DoubleBufferOn();
+        _legendRenderWindow->SwapBuffersOff();
+#endif
+    }
+
+    _legendRenderWindow->SetSize(width, height);
+
+    if (_legendRenderer == NULL) {
+        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
+        _legendRenderWindow->AddRenderer(_legendRenderer);
+    }
+    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
+
+    if (_scalarBarActor == NULL) {
+        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
+        _scalarBarActor->DrawFrameOff();
+        _scalarBarActor->DrawBackgroundOff();
+        _legendRenderer->AddViewProp(_scalarBarActor);
+    }
+
+    if (opaque) {
+        _scalarBarActor->UseOpacityOff();
+    } else {
+        _scalarBarActor->UseOpacityOn();
+    }
+
+    if (width > height) {
+        _scalarBarActor->SetOrientationToHorizontal();
+    } else {
+        _scalarBarActor->SetOrientationToVertical();
+    }
+
+    // Set viewport-relative width/height/pos
+    if (title.empty() && numLabels == 0) {
+        _scalarBarActor->SetBarRatio(1);
+        _scalarBarActor->SetTitleRatio(0);
+        if (width > height) {
+            // horizontal
+            _scalarBarActor->SetDisplayPosition(0, 0);
+            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
+            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+4, height);
+        } else {
+            // vertical
+            _scalarBarActor->SetDisplayPosition(0, -4);
+            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
+            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+1, height+5);
+        }
+    } else {
+        _scalarBarActor->SetBarRatio(0.375);
+        _scalarBarActor->SetTitleRatio(0.5);
+        _scalarBarActor->SetDisplayPosition(0, 0);
+        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
+        _scalarBarActor->GetPosition2Coordinate()->SetValue(width, height);
+    }
+
+    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
+    DataSet *dataSet = NULL;
+    bool cumulative = _useCumulativeRange;
+    if (dataSetID.compare("all") == 0) {
+        if (_dataSets.empty()) {
+            WARN("No DataSets exist, can't fill title or range");
+        } else {
+            dataSet = _dataSets.begin()->second;
+        }
+        cumulative = true;
+    } else {
+        dataSet = getDataSet(dataSetID);
+        if (dataSet == NULL) {
+            ERROR("DataSet '%s' not found", dataSetID.c_str());
+            return false;
+        }
+    }
+
+    bool drawTitle = false;
+    if (!title.empty()) {
+        drawTitle = true;
+        if (title.compare("#auto") == 0) {
+            title.clear();
+        }
+    }
+
+    bool needRange = false;
+    if (range[0] > range[1]) {
+        range[0] = 0.0;
+        range[1] = 1.0;
+        needRange = true;
+    } 
+
+    switch (legendType) {
+    case LEGEND_VECTOR_MAGNITUDE:
+        if (needRange) {
+            if (cumulative) {
+                getCumulativeDataRange(range, fieldName, type, 3);
+            } else if (dataSet != NULL) {
+                dataSet->getDataRange(range, fieldName, type);
+            }
+        }
+
+        lut->SetRange(range);
+
+        if (title.empty() && dataSet != NULL) {
+            if (fieldName != NULL) {
+                title = fieldName;
+                title.append("(mag)");
+            }
+        }
+        break;
+    case LEGEND_VECTOR_X:
+        if (needRange) {
+            if (cumulative) {
+                getCumulativeDataRange(range, fieldName, type, 3, 0);
+            } else if (dataSet != NULL) {
+                dataSet->getDataRange(range, fieldName, type, 0);
+            }
+        }
+
+        lut->SetRange(range);
+
+        if (title.empty() && dataSet != NULL) {
+            if (fieldName != NULL) {
+                title = fieldName;
+                title.append("(x)");
+            }
+        }
+        break;
+    case LEGEND_VECTOR_Y:
+        if (needRange) {
+            if (cumulative) {
+                getCumulativeDataRange(range, fieldName, type, 3, 1);
+            } else if (dataSet != NULL) {
+                dataSet->getDataRange(range, fieldName, type, 1);
+            }
+        }
+
+        lut->SetRange(range);
+
+        if (title.empty() && dataSet != NULL) {
+            if (fieldName != NULL) {
+                title = fieldName;
+                title.append("(y)");
+            }
+        }
+        break;
+    case LEGEND_VECTOR_Z:
+        if (needRange) {
+            if (cumulative) {
+                getCumulativeDataRange(range, fieldName, type, 3, 2);
+            } else if (dataSet != NULL) {
+                dataSet->getDataRange(range, fieldName, type, 1);
+            }
+        }
+
+        lut->SetRange(range);
+
+        if (title.empty() && dataSet != NULL) {
+            if (fieldName != NULL) {
+                title = fieldName;
+                title.append("(z)");
+            }
+        }
+        break;
+    case LEGEND_SCALAR:
+    default:
+        if (needRange) {
+            if (cumulative) {
+                getCumulativeDataRange(range, fieldName, type, 1);
+            } else if (dataSet != NULL) {
+                dataSet->getDataRange(range, fieldName, type);
+            }
+        }
+
+        lut->SetRange(range);
+
+        if (title.empty() && dataSet != NULL) {
+            if (fieldName != NULL)
+                title = fieldName;
+        }
+        break;
+    }
+
+    _scalarBarActor->SetLookupTable(lut);
+    _scalarBarActor->SetMaximumNumberOfColors((width > height ? width : height));
+
+    if (drawTitle) {
+        _scalarBarActor->SetTitle(title.c_str());
+    } else {
+        _scalarBarActor->SetTitle("");
+    }
+
+    double color[3];
+    color[0] = 1 - _bgColor[0];
+    color[1] = 1 - _bgColor[1];
+    color[2] = 1 - _bgColor[2];
+
+    _scalarBarActor->GetTitleTextProperty()->SetColor(color);
+    _scalarBarActor->GetTitleTextProperty()->BoldOff();
+    _scalarBarActor->GetTitleTextProperty()->ItalicOff();
+    _scalarBarActor->GetTitleTextProperty()->ShadowOff();
+    _scalarBarActor->SetNumberOfLabels(numLabels);
+    _scalarBarActor->GetLabelTextProperty()->SetColor(color);
+    _scalarBarActor->GetLabelTextProperty()->BoldOff();
+    _scalarBarActor->GetLabelTextProperty()->ItalicOff();
+    _scalarBarActor->GetLabelTextProperty()->ShadowOff();
+    if (!drawTitle && numLabels == 0) {
+        _scalarBarActor->DrawAnnotationsOff();
+        _scalarBarActor->SetAnnotationLeaderPadding(0);
+        _scalarBarActor->SetTextPad(0);
+    } else {
+        _scalarBarActor->DrawAnnotationsOn();
+        _scalarBarActor->SetAnnotationLeaderPadding(8);
+        _scalarBarActor->SetTextPad(1);
+    }
+
+    _legendRenderWindow->Render();
+    int *sz = _legendRenderWindow->GetSize();
+    if (sz[0] != width || sz[1] != height) {
+        ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], width, height);
+    }
+
+#ifdef RENDER_TARGA
+    _legendRenderWindow->MakeCurrent();
+    // Must clear previous errors first.
+    while (glGetError() != GL_NO_ERROR){
+        ;
+    }
+    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
+    int size = bytesPerPixel * width * height;
+
+    if (imgData->GetMaxId() + 1 != size)
+    {
+        imgData->SetNumberOfComponents(bytesPerPixel);
+        imgData->SetNumberOfValues(size);
+    }
+    glDisable(GL_TEXTURE_2D);
+    if (_legendRenderWindow->GetDoubleBuffer()) {
+        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
+    } else {
+        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
+    }
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
+    if (bytesPerPixel == 4) {
+        glReadPixels(0, 0, width, height, GL_BGRA,
+                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
+    } else {
+        glReadPixels(0, 0, width, height, GL_BGR,
+                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
+    }
+    if (glGetError() != GL_NO_ERROR) {
+        ERROR("glReadPixels");
+    }
+#else
+    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
+                                      !_legendRenderWindow->GetDoubleBuffer(),
+                                      imgData);
+#endif
+#endif
+    TRACE("Leave");
+    return true;
+}
+
+bool Renderer::renderColorMap(const ColorMapId& id,
+                              int width, int height,
+                              bool opaque,
+                              vtkUnsignedCharArray *imgData)
+{
+    TRACE("Enter");
+    ColorMap *colorMap = getColorMap(id);
+    if (colorMap == NULL)
+        return false;
+#ifdef LEGEND_SOFTWARE_RENDER
+    ColorMap::renderColorMap(colorMap, width, height, imgData, opaque, _bgColor,
+#ifdef RENDER_TARGA
+                             true, TARGA_BYTES_PER_PIXEL
+#else
+                             false
+#endif
+                             );
+#else
+    if (_legendRenderWindow == NULL) {
+        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
+        _legendRenderWindow->SetMultiSamples(0);
+#ifdef USE_OFFSCREEN_RENDERING
+        _legendRenderWindow->DoubleBufferOff();
+        _legendRenderWindow->OffScreenRenderingOn();
+#else
+        _legendRenderWindow->DoubleBufferOn();
+        _legendRenderWindow->SwapBuffersOff();
+#endif
+    }
+
+    _legendRenderWindow->SetSize(width, height);
+
+    if (_legendRenderer == NULL) {
+        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
+        _legendRenderWindow->AddRenderer(_legendRenderer);
+    }
+    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
+
+    if (_scalarBarActor == NULL) {
+        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
+        _scalarBarActor->DrawFrameOff();
+        _scalarBarActor->DrawBackgroundOff();
+        _scalarBarActor->DrawColorBarOn();
+        _legendRenderer->AddViewProp(_scalarBarActor);
+    }
+
+    if (opaque) {
+        _scalarBarActor->UseOpacityOff();
+    } else {
+        _scalarBarActor->UseOpacityOn();
+    }
+
+    if (width > height) {
+        _scalarBarActor->SetOrientationToHorizontal();
+    } else {
+        _scalarBarActor->SetOrientationToVertical();
+    }
+
+    // Set viewport-relative width/height/pos
+    _scalarBarActor->SetBarRatio(1);
+    _scalarBarActor->SetTitleRatio(0);
+    if (width > height) {
+        // horizontal
+        _scalarBarActor->SetDisplayPosition(0, 0);
+        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
+        _scalarBarActor->GetPosition2Coordinate()->SetValue(width+4, height);
+    } else {
+        // vertical
+        _scalarBarActor->SetDisplayPosition(0, -4);
+        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
+        _scalarBarActor->GetPosition2Coordinate()->SetValue(width+1, height+5);
+    }
+
+    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
+
+    double range[2];
+    range[0] = 0.0;
+    range[1] = 1.0;
+
+    lut->SetRange(range);
+
+    _scalarBarActor->SetLookupTable(lut);
+    _scalarBarActor->SetMaximumNumberOfColors((width > height ? width : height));
+    _scalarBarActor->SetTitle("");
+    _scalarBarActor->SetNumberOfLabels(0);
+    _scalarBarActor->DrawAnnotationsOff();
+    _scalarBarActor->SetAnnotationLeaderPadding(0);
+    _scalarBarActor->SetTextPad(0);
+
+    _legendRenderWindow->Render();
+    int *sz = _legendRenderWindow->GetSize();
+    if (sz[0] != width || sz[1] != height) {
+        ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], width, height);
+    }
+
+#ifdef RENDER_TARGA
+    _legendRenderWindow->MakeCurrent();
+    // Must clear previous errors first.
+    while (glGetError() != GL_NO_ERROR){
+        ;
+    }
+    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
+    int size = bytesPerPixel * width * height;
+
+    if (imgData->GetMaxId() + 1 != size)
+    {
+        imgData->SetNumberOfComponents(bytesPerPixel);
+        imgData->SetNumberOfValues(size);
+    }
+    glDisable(GL_TEXTURE_2D);
+    if (_legendRenderWindow->GetDoubleBuffer()) {
+        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
+    } else {
+        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
+    }
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
+    if (bytesPerPixel == 4) {
+        glReadPixels(0, 0, width, height, GL_BGRA,
+                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
+    } else {
+        glReadPixels(0, 0, width, height, GL_BGR,
+                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
+    }
+    if (glGetError() != GL_NO_ERROR) {
+        ERROR("glReadPixels");
+    }
+#else
+    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
+                                      !_legendRenderWindow->GetDoubleBuffer(),
+                                      imgData);
+#endif
+#endif
+    TRACE("Leave");
+    return true;
+}
+
+/**
+ * \brief Set camera FOV based on render window height
+ * 
+ * Computes a field-of-view angle based on some assumptions about
+ * viewer's distance to screen and pixel density
+ */
+void Renderer::setViewAngle(int height)
+{
+    // Distance of eyes from screen in inches
+    double d = 20.0;
+    // Assume 72 ppi screen
+    double h = (double)height / 72.0;
+
+    double angle = vtkMath::DegreesFromRadians(2.0 * atan((h/2.0)/d));
+    _renderer->GetActiveCamera()->SetViewAngle(angle);
+
+    TRACE("Setting view angle: %g", angle);
+}
+
+/**
+ * \brief Resize the render window (image size for renderings)
+ */
+void Renderer::setWindowSize(int width, int height)
+{
+    if (_windowWidth == width &&
+        _windowHeight == height) {
+        TRACE("No change");
+        return;
+    }
+
+    TRACE("Setting window size to %dx%d", width, height);
+
+    //setViewAngle(height);
+
+    // FIXME: Fix up panning on aspect change
+#ifdef notdef
+    if (_cameraPan[0] != 0.0) {
+        _cameraPan[0] *= ((double)_windowWidth / width);
+    }
+    if (_cameraPan[1] != 0.0) {
+        _cameraPan[1] *= ((double)_windowHeight / height);
+    }
+#endif
+    _windowWidth = width;
+    _windowHeight = height;
+    _renderWindow->SetSize(_windowWidth, _windowHeight);
+
+    if (_cameraMode == IMAGE) {
+        if (_cameraAspect == ASPECT_WINDOW) {
+            double imgWindowAspect = getImageCameraAspect();
+            TRACE("Setting object aspect to %g", imgWindowAspect);
+            setObjectAspects(imgWindowAspect);
+            initCamera();
+        } else {
+            if (_userImgWorldDims[0] > 0) {
+                _setCameraZoomRegion(_userImgWorldOrigin[0],
+                                     _userImgWorldOrigin[1],
+                                     _userImgWorldDims[0],
+                                     _userImgWorldDims[1]);
+            } else {
+                if (isCameraMaximized()) {
+                    initCamera();
+                } else {
+                    _setCameraZoomRegion(_imgWorldOrigin[0],
+                                         _imgWorldOrigin[1],
+                                         _imgWorldDims[0],
+                                         _imgWorldDims[1]);
+                }
+            }
+        }
+    }
+    _needsRedraw = true;
+}
+
+void Renderer::setObjectAspects(double aspectRatio)
+{
+    setGraphicsObjectAspect<Arc>(aspectRatio);
+    setGraphicsObjectAspect<Arrow>(aspectRatio);
+    setGraphicsObjectAspect<Box>(aspectRatio);
+    setGraphicsObjectAspect<Cone>(aspectRatio);
+    setGraphicsObjectAspect<Contour2D>(aspectRatio);
+    setGraphicsObjectAspect<Contour3D>(aspectRatio);
+    setGraphicsObjectAspect<Cutplane>(aspectRatio);
+    setGraphicsObjectAspect<Cylinder>(aspectRatio);
+    setGraphicsObjectAspect<Disk>(aspectRatio);
+    setGraphicsObjectAspect<Glyphs>(aspectRatio);
+    setGraphicsObjectAspect<HeightMap>(aspectRatio);
+    setGraphicsObjectAspect<Image>(aspectRatio);
+    setGraphicsObjectAspect<ImageCutplane>(aspectRatio);
+    setGraphicsObjectAspect<LIC>(aspectRatio);
+    setGraphicsObjectAspect<Line>(aspectRatio);
+    setGraphicsObjectAspect<Molecule>(aspectRatio);
+    setGraphicsObjectAspect<Outline>(aspectRatio);
+    setGraphicsObjectAspect<Parallelepiped>(aspectRatio);
+    setGraphicsObjectAspect<PolyData>(aspectRatio);
+    setGraphicsObjectAspect<Polygon>(aspectRatio);
+    setGraphicsObjectAspect<PseudoColor>(aspectRatio);
+    setGraphicsObjectAspect<Sphere>(aspectRatio);
+    setGraphicsObjectAspect<Streamlines>(aspectRatio);
+    setGraphicsObjectAspect<Text3D>(aspectRatio);
+    setGraphicsObjectAspect<Volume>(aspectRatio);
+    setGraphicsObjectAspect<Warp>(aspectRatio);
+}
+
+void Renderer::setCameraAspect(Aspect aspect)
+{
+    //if (_cameraAspect == aspect)
+    //    return;
+
+    _cameraAspect = aspect;
+
+    double aspectRatio = 0.0;
+    switch (aspect) {
+    case ASPECT_SQUARE:
+        aspectRatio = 1.0;
+        break;
+    case ASPECT_WINDOW:
+        aspectRatio = 1.0;
+        if (_cameraMode == IMAGE) {
+            aspectRatio = getImageCameraAspect();
+        }
+        break;
+    case ASPECT_NATIVE:
+    default:
+        aspectRatio = 0.0;
+    }
+
+    setObjectAspects(aspectRatio);
+
+    if (_cameraMode == IMAGE)
+        _needsCameraReset = true;
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Change the camera type: perspective, orthographic or image view
+ *
+ * Perspective mode is a normal 3D camera.
+ *
+ * Orthogrphic mode is parallel projection.
+ *
+ * Image mode is an orthographic camera with fixed axes and a clipping region 
+ * around the plot area, use _setCameraZoomRegion to control the displayed area
+ *
+ * \param[in] mode Enum specifying camera type
+ */
+void Renderer::setCameraMode(CameraMode mode)
+{
+    if (_cameraMode == mode) return;
+
+    CameraMode origMode = _cameraMode;
+    _cameraMode = mode;
+    resetAxes();
+
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    switch (mode) {
+    case ORTHO: {
+        TRACE("Set camera to Ortho mode");
+        camera->ParallelProjectionOn();
+        if (origMode == IMAGE) {
+            resetCamera(true);
+        }
+        break;
+    }
+    case PERSPECTIVE: {
+        TRACE("Set camera to Perspective mode");
+        camera->ParallelProjectionOff();
+        if (origMode == IMAGE) {
+            resetCamera(true);
+        }
+        break;
+    }
+    case IMAGE: {
+        camera->ParallelProjectionOn();
+        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
+                             _imgWorldDims[0],_imgWorldDims[1]);
+        TRACE("Set camera to Image mode");
+        break;
+    }
+    default:
+        ERROR("Unkown camera mode: %d", mode);
+    }
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Get the current camera mode
+ */
+Renderer::CameraMode Renderer::getCameraMode() const
+{
+    return _cameraMode;
+}
+
+/**
+ * \brief Set the VTK camera parameters based on a 4x4 view matrix
+ */
+void Renderer::setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4& mat)
+{
+    double d = camera->GetDistance();
+    double vu[3];
+    vu[0] = mat.GetElement(1, 0);
+    vu[1] = mat.GetElement(1, 1);
+    vu[2] = mat.GetElement(1, 2);
+    double trans[3];
+    trans[0] = mat.GetElement(0, 3);
+    trans[1] = mat.GetElement(1, 3);
+    trans[2] = mat.GetElement(2, 3);
+    mat.SetElement(0, 3, 0.0);
+    mat.SetElement(1, 3, 0.0);
+    mat.SetElement(2, 3, 0.0);
+    double vpn[3] = {mat.GetElement(2, 0), mat.GetElement(2, 1), mat.GetElement(2, 2)};
+    double pos[3];
+    // With translation removed, we have an orthogonal matrix, 
+    // so the inverse is the transpose
+    mat.Transpose();
+    mat.MultiplyPoint(trans, pos);
+    pos[0] = -pos[0];
+    pos[1] = -pos[1];
+    pos[2] = -pos[2];
+    double fp[3];
+    fp[0] = pos[0] - vpn[0] * d;
+    fp[1] = pos[1] - vpn[1] * d;
+    fp[2] = pos[2] - vpn[2] * d;
+    camera->SetPosition(pos);
+    camera->SetFocalPoint(fp);
+    camera->SetViewUp(vu);
+}
+
+/**
+ * \brief Set the orientation of the camera from a quaternion
+ * rotation
+ *
+ * \param[in] quat A quaternion with scalar part first: w,x,y,z
+ * \param[in] absolute Is rotation absolute or relative?
+ */
+void Renderer::setCameraOrientation(const double quat[4], bool absolute)
+{
+    if (_cameraMode == IMAGE)
+        return;
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+    vtkSmartPointer<vtkMatrix4x4> rotMat = vtkSmartPointer<vtkMatrix4x4>::New();
+
+    double q[4];
+    copyQuat(quat, q);
+
+    if (absolute) {
+        double abs[4];
+        // Save absolute rotation
+        copyQuat(q, abs);
+        // Compute relative rotation
+        quatMult(quatReciprocal(_cameraOrientation), q, q);
+        // Store absolute rotation
+        copyQuat(abs, _cameraOrientation);
+    } else {
+        // Compute new absolute rotation
+        quatMult(_cameraOrientation, q, _cameraOrientation);
+    }
+
+    quaternionToTransposeMatrix4x4(q, *rotMat);
+#ifdef DEBUG
+    TRACE("Rotation matrix:\n %g %g %g\n %g %g %g\n %g %g %g",
+          (*rotMat)[0][0], (*rotMat)[0][1], (*rotMat)[0][2],
+          (*rotMat)[1][0], (*rotMat)[1][1], (*rotMat)[1][2],
+          (*rotMat)[2][0], (*rotMat)[2][1], (*rotMat)[2][2]);
+    vtkSmartPointer<vtkMatrix4x4> camMat = vtkSmartPointer<vtkMatrix4x4>::New();
+    camMat->DeepCopy(camera->GetViewTransformMatrix());
+    TRACE("Camera matrix:\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g",
+          (*camMat)[0][0], (*camMat)[0][1], (*camMat)[0][2], (*camMat)[0][3],
+          (*camMat)[1][0], (*camMat)[1][1], (*camMat)[1][2], (*camMat)[1][3],
+          (*camMat)[2][0], (*camMat)[2][1], (*camMat)[2][2], (*camMat)[2][3],
+          (*camMat)[3][0], (*camMat)[3][1], (*camMat)[3][2], (*camMat)[3][3]);
+    printCameraInfo(camera);
+#endif
+    trans->Translate(0, 0, -camera->GetDistance());
+    trans->Concatenate(rotMat);
+    trans->Translate(0, 0, camera->GetDistance());
+    trans->Concatenate(camera->GetViewTransformMatrix());
+    setCameraFromMatrix(camera, *trans->GetMatrix());
+
+    resetCameraClippingRange();
+    printCameraInfo(camera);
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the position and orientation of the camera
+ *
+ * \param[in] position x,y,z position of camera in world coordinates
+ * \param[in] focalPoint x,y,z look-at point in world coordinates
+ * \param[in] viewUp x,y,z up vector of camera
+ */
+void Renderer::setCameraOrientationAndPosition(const double position[3],
+                                               const double focalPoint[3],
+                                               const double viewUp[3])
+{
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    camera->SetPosition(position);
+    camera->SetFocalPoint(focalPoint);
+    camera->SetViewUp(viewUp);
+    resetCameraClippingRange();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Get the position and orientation of the camera
+ *
+ * \param[out] position x,y,z position of camera in world coordinates
+ * \param[out] focalPoint x,y,z look-at point in world coordinates
+ * \param[out] viewUp x,y,z up vector of camera
+ */
+void Renderer::getCameraOrientationAndPosition(double position[3],
+                                               double focalPoint[3],
+                                               double viewUp[3])
+{
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    camera->GetPosition(position);
+    camera->GetFocalPoint(focalPoint);
+    camera->GetViewUp(viewUp);
+}
+
+void Renderer::sceneBoundsChanged()
+{
+#ifdef RESET_CAMERA_ON_SCENE_CHANGE
+    _needsCameraReset = true;
+#else
+    _needsAxesReset = true;
+    _needsCameraClippingRangeReset = true;
+#endif
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Reset pan, zoom, clipping planes and optionally rotation
+ *
+ * \param[in] resetOrientation Reset the camera rotation/orientation also
+ */
+void Renderer::resetCamera(bool resetOrientation)
+{
+    TRACE("Enter: %d", resetOrientation ? 1 : 0);
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    if (_cameraMode == IMAGE) {
+        initCamera();
+    } else {
+        if (resetOrientation) {
+            camera->SetPosition(0, 0, 1);
+            camera->SetFocalPoint(0, 0, 0);
+            camera->SetViewUp(0, 1, 0);
+            _cameraOrientation[0] = 1.0;
+            _cameraOrientation[1] = 0.0;
+            _cameraOrientation[2] = 0.0;
+            _cameraOrientation[3] = 0.0;
+        }
+        //setViewAngle(_windowHeight);
+        //double bounds[6];
+        //collectBounds(bounds, _computeOnlyVisibleBounds);
+        //_renderer->ResetCamera(bounds);
+        if (_needsAxesReset) {
+            resetAxes();
+            _needsAxesReset = false;
+        }
+        resetVtkCamera();
+        //computeScreenWorldCoords();
+    }
+
+    printCameraInfo(camera);
+
+    _cameraZoomRatio = 1;
+    _cameraPan[0] = 0;
+    _cameraPan[1] = 0;
+
+    _needsRedraw = true;
+}
+
+void Renderer::resetVtkCamera(double *bounds)
+{
+    TRACE("Enter: bounds: %p", bounds);
+    if (bounds != NULL)
+        _renderer->ResetCamera(bounds);
+    else
+        _renderer->ResetCamera();
+    printCameraInfo(_renderer->GetActiveCamera());
+}
+
+/**
+ * \brief Set the camera near/far clipping range based on current scene bounds
+ */
+void Renderer::resetCameraClippingRange()
+{
+    _renderer->ResetCameraClippingRange();
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    //double dist = camera->GetClippingRange()[0] + (camera->GetClippingRange()[1] - camera->GetClippingRange()[0])/2.0;
+    //camera->SetDistance(dist);
+    printCameraInfo(camera);
+}
+
+/**
+ * \brief Perform a relative rotation to current camera orientation
+ *
+ * Angles are in degrees, rotation is about focal point
+ */
+void Renderer::rotateCamera(double yaw, double pitch, double roll)
+{
+    if (_cameraMode == IMAGE)
+        return;
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    camera->Azimuth(yaw); // Rotate about object
+    //camera->SetYaw(yaw); // Rotate about camera
+    camera->Elevation(pitch); // Rotate about object
+    //camera->SetPitch(pitch); // Rotate about camera
+    camera->Roll(roll); // Roll about camera view axis
+    resetCameraClippingRange();
+    //computeScreenWorldCoords();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Perform a 2D translation of the camera
+ *
+ * x,y pan amount are specified as signed absolute pan amount in viewport
+ * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
+ * etc.
+ *
+ * \param[in] x Viewport coordinate horizontal panning (positive number pans
+ * camera left, object right)
+ * \param[in] y Viewport coordinate vertical panning (positive number pans 
+ * camera up, object down)
+ * \param[in] absolute Control if pan amount is relative to current or absolute
+ */
+void Renderer::panCamera(double x, double y, bool absolute)
+{
+    TRACE("Enter panCamera: %g %g, current abs: %g %g",
+          x, y, _cameraPan[0], _cameraPan[1]);
+
+    if (_cameraMode == IMAGE) {
+        _userImgWorldOrigin[0] = 0;
+        _userImgWorldOrigin[1] = 0;
+        _userImgWorldDims[0] = -1;
+        _userImgWorldDims[1] = -1;
+
+        // Reverse x rather than y, since we are panning the camera, and client
+        // expects to be panning/moving the object
+        x = -x * _screenWorldCoords[2];
+        y = y * _screenWorldCoords[3];
+
+        if (absolute) {
+            double panAbs[2];
+            panAbs[0] = x;
+            panAbs[1] = y;
+            x -= _cameraPan[0];
+            y -= _cameraPan[1];
+            _cameraPan[0] = panAbs[0];
+            _cameraPan[1] = panAbs[1];
+        } else {
+            _cameraPan[0] += x;
+            _cameraPan[1] += y;
+        }
+
+        _imgWorldOrigin[0] += x;
+        _imgWorldOrigin[1] += y;
+        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
+                             _imgWorldDims[0], _imgWorldDims[1]);
+    } else {
+        y = -y;
+        if (absolute) {
+            double panAbs[2];
+            panAbs[0] = x;
+            panAbs[1] = y;
+            x -= _cameraPan[0];
+            y -= _cameraPan[1];
+            _cameraPan[0] = panAbs[0];
+            _cameraPan[1] = panAbs[1];
+        } else {
+            _cameraPan[0] += x;
+            _cameraPan[1] += y;
+        }
+
+        if (x != 0.0 || y != 0.0) {
+            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+            double viewFocus[4], focalDepth, viewPoint[3];
+            double newPickPoint[4], oldPickPoint[4], motionVector[3];
+
+            camera->GetFocalPoint(viewFocus);
+            computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], 
+                                  viewFocus);
+            focalDepth = viewFocus[2];
+
+            computeDisplayToWorld((x * 2. + 1.) * (double)_windowWidth / 2.0,
+                                  (y * 2. + 1.) * (double)_windowHeight / 2.0,
+                                  focalDepth, 
+                                  newPickPoint);
+
+            computeDisplayToWorld((double)_windowWidth / 2.0,
+                                  (double)_windowHeight / 2.0,
+                                  focalDepth, 
+                                  oldPickPoint);
+
+            // Camera motion is reversed
+            motionVector[0] = oldPickPoint[0] - newPickPoint[0];
+            motionVector[1] = oldPickPoint[1] - newPickPoint[1];
+            motionVector[2] = oldPickPoint[2] - newPickPoint[2];
+
+            camera->GetFocalPoint(viewFocus);
+            camera->GetPosition(viewPoint);
+            camera->SetFocalPoint(motionVector[0] + viewFocus[0],
+                                  motionVector[1] + viewFocus[1],
+                                  motionVector[2] + viewFocus[2]);
+
+            camera->SetPosition(motionVector[0] + viewPoint[0],
+                                motionVector[1] + viewPoint[1],
+                                motionVector[2] + viewPoint[2]);
+
+            resetCameraClippingRange();
+            //computeScreenWorldCoords();
+        }
+    }
+
+    TRACE("Leave panCamera: %g %g, current abs: %g %g",
+          x, y, _cameraPan[0], _cameraPan[1]);
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Dolly camera or set orthographic scaling based on camera type
+ *
+ * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
+ * \param[in] absolute Control if zoom factor is relative to current setting or absolute
+ */
+void Renderer::zoomCamera(double z, bool absolute)
+{
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    TRACE("Enter Zoom: current abs: %g, z: %g, view angle %g",
+          _cameraZoomRatio, z, camera->GetViewAngle());
+
+    if (absolute) {
+        assert(_cameraZoomRatio > 0.0);
+        double zAbs = z;
+        z *= 1.0/_cameraZoomRatio;
+        _cameraZoomRatio = zAbs;
+    } else {
+        _cameraZoomRatio *= z;
+    }
+
+    if (_cameraMode == IMAGE) {
+        _userImgWorldOrigin[0] = 0;
+        _userImgWorldOrigin[1] = 0;
+        _userImgWorldDims[0] = -1;
+        _userImgWorldDims[1] = -1;
+
+        double dx = _imgWorldDims[0];
+        double dy = _imgWorldDims[1];
+        _imgWorldDims[0] /= z;
+        _imgWorldDims[1] /= z;
+        dx -= _imgWorldDims[0];
+        dy -= _imgWorldDims[1];
+        _imgWorldOrigin[0] += dx/2.0;
+        _imgWorldOrigin[1] += dy/2.0;
+        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
+                             _imgWorldDims[0], _imgWorldDims[1]);
+    } else {
+        // Keep ortho and perspective modes in sync
+        // Move camera forward/back for perspective camera
+        camera->Dolly(z);
+        // Change ortho parallel scale
+        camera->SetParallelScale(camera->GetParallelScale()/z);
+        resetCameraClippingRange();
+        //computeScreenWorldCoords();
+    }
+
+    TRACE("Leave Zoom: rel %g, new abs: %g, view angle %g", 
+          z, _cameraZoomRatio, camera->GetViewAngle());
+
+    _needsRedraw = true;
+}
+
+bool Renderer::setCameraZoomRegionPixels(int x, int y, int width, int height)
+{
+    if (_cameraMode != IMAGE)
+        return false;
+
+    double wx, wy, ww, wh;
+
+    y = _windowHeight - y;
+    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
+    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
+
+    wx = _screenWorldCoords[0] + x * pxToWorldX;
+    wy = _screenWorldCoords[1] + y * pxToWorldY;
+    ww = abs(width) *  pxToWorldX;
+    wh = abs(height) * pxToWorldY;
+    setCameraZoomRegion(wx, wy, ww, wh);
+
+    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
+          x, y, width, height,
+          wx, wy, ww, wh);
+
+    return true;
+}
+
+bool Renderer::setCameraZoomRegion(double x, double y, double width, double height)
+{
+    if (_cameraMode != IMAGE)
+        return false;
+
+    _userImgWorldOrigin[0] = x;
+    _userImgWorldOrigin[1] = y;
+    _userImgWorldDims[0] = width;
+    _userImgWorldDims[1] = height;
+    _setCameraZoomRegion(x, y, width, height);
+
+    return true;
+}
+
+/**
+ * \brief Set the pan/zoom using a corner and dimensions in pixel coordinates
+ * 
+ * \param[in] x left pixel coordinate
+ * \param[in] y bottom  pixel coordinate (with y=0 at top of window)
+ * \param[in] width Width of zoom region in pixel coordinates
+ * \param[in] height Height of zoom region in pixel coordinates
+ */
+void Renderer::_setCameraZoomRegionPixels(int x, int y, int width, int height)
+{
+    if (_cameraMode != IMAGE) {
+        ERROR("Called while camera mode is not image");
+        return;
+    }
+
+    double wx, wy, ww, wh;
+
+    y = _windowHeight - y;
+    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
+    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
+
+    wx = _screenWorldCoords[0] + x * pxToWorldX;
+    wy = _screenWorldCoords[1] + y * pxToWorldY;
+    ww = abs(width) *  pxToWorldX;
+    wh = abs(height) * pxToWorldY;
+    _setCameraZoomRegion(wx, wy, ww, wh);
+
+    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
+          x, y, width, height,
+          wx, wy, ww, wh);
+}
+
+void Renderer::getImageCameraSizes(int *imgWidthPx, int *imgHeightPx,
+                                   int *_pxOffsetX, int *_pxOffsetY)
+{
+    int pxOffsetX, pxOffsetY;
+    pxOffsetX = (int)(0.17 * (double)_windowWidth);
+    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
+    pxOffsetY = (int)(0.15 * (double)_windowHeight);
+    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
+
+    int outerGutter = (int)(0.03 * (double)_windowWidth);
+    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
+
+    *imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
+    *imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
+    if (_pxOffsetX != NULL) *_pxOffsetX = pxOffsetX;
+    if (_pxOffsetY != NULL) *_pxOffsetY = pxOffsetY;
+}
+
+double Renderer::getImageCameraAspect()
+{
+    int imgWidthPx, imgHeightPx;
+    getImageCameraSizes(&imgWidthPx, &imgHeightPx);
+    return ((double)imgWidthPx / (double)imgHeightPx);
+}
+
+/**
+ * \brief Set the pan/zoom using a corner and dimensions in world coordinates
+ * 
+ * \param[in] x left world coordinate
+ * \param[in] y bottom  world coordinate
+ * \param[in] width Width of zoom region in world coordinates
+ * \param[in] height Height of zoom region in world coordinates
+ */
+void Renderer::_setCameraZoomRegion(double x, double y, double width, double height)
+{
+    if (_cameraMode != IMAGE) {
+        ERROR("Called while camera mode is not image");
+        return;
+    }
+
+    int imgWidthPx, imgHeightPx;
+    int pxOffsetX, pxOffsetY;
+
+    getImageCameraSizes(&imgWidthPx, &imgHeightPx, &pxOffsetX, &pxOffsetY);
+
+    double imgWindowAspect = (double)imgWidthPx / (double)imgHeightPx;
+
+    double pxToWorld;
+    double imgWidthWorld;
+    double imgHeightWorld;
+
+    double requestedAspect = width / height;
+
+    if (requestedAspect >= imgWindowAspect) {
+        pxToWorld = width / (double)imgWidthPx;
+        imgWidthWorld = width;
+        imgHeightWorld = (double)imgHeightPx * pxToWorld;
+    } else {
+        pxToWorld = height / (double)imgHeightPx;
+        imgWidthWorld = (double)imgWidthPx * pxToWorld;
+        imgHeightWorld =  height;
+    }
+
+    double offsetX = pxOffsetX * pxToWorld;
+    double offsetY = pxOffsetY * pxToWorld;
+    double winWidthWorld = _windowWidth * pxToWorld;
+    double winHeightWorld = _windowHeight * pxToWorld;
+
+    TRACE("Window: %d %d", _windowWidth, _windowHeight);
+    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
+    TRACE("pxToWorld: %g", pxToWorld);
+    TRACE("offset: %g %g", offsetX, offsetY);
+
+    _imgWorldOrigin[0] = x;
+    _imgWorldOrigin[1] = y;
+    _imgWorldDims[0] = width;
+    _imgWorldDims[1] = height;
+    _imgWindowWorldDims[0] = imgWidthWorld;
+    _imgWindowWorldDims[1] = imgHeightWorld;
+
+    double camPos[2];
+    camPos[0] = _imgWorldOrigin[0] - offsetX + winWidthWorld / 2.0;
+    camPos[1] = _imgWorldOrigin[1] - offsetY + winHeightWorld / 2.0;
+
+    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
+    camera->ParallelProjectionOn();
+    camera->SetClippingRange(0.5, 1.5);
+    // Half of world coordinate height of viewport (Documentation is wrong)
+    camera->SetParallelScale(winHeightWorld / 2.0);
+
+    if (_imgCameraPlane == PLANE_XY) {
+        // XY plane
+        camera->SetPosition(camPos[0], camPos[1], _imgCameraOffset + 1.);
+        camera->SetFocalPoint(camPos[0], camPos[1], _imgCameraOffset);
+        camera->SetViewUp(0, 1, 0);
+        // bottom
+        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
+        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
+        // left
+        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
+        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
+        // top
+        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
+        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
+        // right
+        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWindowWorldDims[0], 0, 0);
+        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
+
+        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
+                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
+                                  _imgCameraOffset, _imgCameraOffset);
+        _cubeAxesActor->XAxisVisibilityOn();
+        _cubeAxesActor->YAxisVisibilityOn();
+        _cubeAxesActor->ZAxisVisibilityOff();
+    } else if (_imgCameraPlane == PLANE_ZY) {
+        // ZY plane
+        camera->SetPosition(_imgCameraOffset - 1., camPos[1], camPos[0]);
+        camera->SetFocalPoint(_imgCameraOffset, camPos[1], camPos[0]);
+        camera->SetViewUp(0, 1, 0);
+        // bottom
+        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
+        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
+        // left
+        _cameraClipPlanes[1]->SetOrigin(0, 0, _imgWorldOrigin[0]);
+        _cameraClipPlanes[1]->SetNormal(0, 0, 1);
+        // top
+        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
+        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
+        // right
+        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
+        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
+
+        _cubeAxesActor->SetBounds(_imgCameraOffset, _imgCameraOffset, 
+                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
+                                  _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
+        _cubeAxesActor->XAxisVisibilityOff();
+        _cubeAxesActor->YAxisVisibilityOn();
+        _cubeAxesActor->ZAxisVisibilityOn();
+    } else {
+        // XZ plane
+        camera->SetPosition(camPos[0], _imgCameraOffset - 1., camPos[1]);
+        camera->SetFocalPoint(camPos[0], _imgCameraOffset, camPos[1]);
+        camera->SetViewUp(0, 0, 1);
+        // bottom
+        _cameraClipPlanes[0]->SetOrigin(0, 0, _imgWorldOrigin[1]);
+        _cameraClipPlanes[0]->SetNormal(0, 0, 1);
+        // left
+        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
+        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
+        // top
+        _cameraClipPlanes[2]->SetOrigin(0, 0, _imgWorldOrigin[1] + _imgWindowWorldDims[1]);
+        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
+        // right
+        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWindowWorldDims[0], 0, 0);
+        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
+
+        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
+                                  _imgCameraOffset, _imgCameraOffset,
+                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1]);
+        _cubeAxesActor->XAxisVisibilityOn();
+        _cubeAxesActor->YAxisVisibilityOff();
+        _cubeAxesActor->ZAxisVisibilityOn();
+    }
+
+    // Fix up axis ranges based on new bounds
+    setAxesRanges();
+
+    // Compute screen world coordinates
+    computeScreenWorldCoords();
+
+#ifdef DEBUG
+    printCameraInfo(camera);
+#endif
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Convert pixel/display coordinates to world coordinates based on current camera
+ */
+void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
+{
+    _renderer->SetDisplayPoint(x, y, z);
+    _renderer->DisplayToWorld();
+    _renderer->GetWorldPoint(worldPt);
+    if (worldPt[3]) {
+        worldPt[0] /= worldPt[3];
+        worldPt[1] /= worldPt[3];
+        worldPt[2] /= worldPt[3];
+        worldPt[3] = 1.0;
+    }
+}
+
+/**
+ * \brief Convert world coordinates to pixel/display coordinates based on current camera
+ */
+void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
+{
+    _renderer->SetWorldPoint(x, y, z, 1.0);
+    _renderer->WorldToDisplay();
+    _renderer->GetDisplayPoint(displayPt);
+}
+
+/**
+ * \brief Compute the world coordinate bounds of the display rectangle
+ */
+void Renderer::computeScreenWorldCoords()
+{
+    // Start with viewport coords [-1,1]
+    double x0 = -1;
+    double y0 = -1;
+    double z0 = -1;
+    double x1 = 1;
+    double y1 = 1;
+    double z1 = -1;
+
+    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
+    double result[4];
+
+    // get the perspective transformation from the active camera
+    mat->DeepCopy(_renderer->GetActiveCamera()->
+                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
+
+    // use the inverse matrix
+    mat->Invert();
+
+    // Transform point to world coordinates
+    result[0] = x0;
+    result[1] = y0;
+    result[2] = z0;
+    result[3] = 1.0;
+
+    mat->MultiplyPoint(result, result);
+
+    // Get the transformed vector & set WorldPoint
+    // while we are at it try to keep w at one
+    if (result[3]) {
+        x0 = result[0] / result[3];
+        y0 = result[1] / result[3];
+        z0 = result[2] / result[3];
+    }
+
+    result[0] = x1;
+    result[1] = y1;
+    result[2] = z1;
+    result[3] = 1.0;
+
+    mat->MultiplyPoint(result, result);
+
+    if (result[3]) {
+        x1 = result[0] / result[3];
+        y1 = result[1] / result[3];
+        z1 = result[2] / result[3];
+    }
+
+    mat->Delete();
+
+    if (_imgCameraPlane == PLANE_XZ) {
+        _screenWorldCoords[0] = x0;
+        _screenWorldCoords[1] = z0;
+        _screenWorldCoords[2] = x1 - x0;
+        _screenWorldCoords[3] = z1 - z0;
+    } else if (_imgCameraPlane == PLANE_ZY) {
+        _screenWorldCoords[0] = z0;
+        _screenWorldCoords[1] = y0;
+        _screenWorldCoords[2] = z1 - z0;
+        _screenWorldCoords[3] = y1 - y0;
+    } else {
+        // XY
+        _screenWorldCoords[0] = x0;
+        _screenWorldCoords[1] = y0;
+        _screenWorldCoords[2] = x1 - x0;
+        _screenWorldCoords[3] = y1 - y0;
+    }
+}
+
+/**
+ * \brief Get the world coordinates of the image camera plot area
+ *
+ * \param[out] xywh Array to hold x,y,width,height world coordinates
+ */
+void Renderer::getCameraZoomRegion(double xywh[4]) const
+{
+    xywh[0] = _imgWorldOrigin[0];
+    xywh[1] = _imgWorldOrigin[1];
+    xywh[2] = _imgWorldDims[0];
+    xywh[3] = _imgWorldDims[1];
+}
+
+/**
+ * \brief Get the world origin and dimensions of the screen
+ *
+ * \param[out] xywh Array to hold x,y,width,height world coordinates
+ */
+void Renderer::getScreenWorldCoords(double xywh[4]) const
+{
+    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
+}
+
+/**
+ * \brief Compute bounding box containing the two input bounding boxes
+ *
+ * \param[out] boundsDest Union of the two input bounding boxes
+ * \param[in] bounds1 Input bounding box
+ * \param[in] bounds2 Input bounding box
+ */
+void Renderer::mergeBounds(double *boundsDest,
+                           const double *bounds1, const double *bounds2)
+{
+    assert(boundsDest != NULL);
+    assert(bounds1 != NULL);
+    if (bounds2 == NULL) {
+        WARN("NULL bounds2 array");
+        return;
+    }
+    bool b1empty = (bounds1[0] > bounds1[1]);
+    bool b2empty = (bounds2[0] > bounds2[1]);
+
+    if (b1empty && b2empty)
+        return;
+    if (b1empty) {
+        memcpy(boundsDest, bounds2, sizeof(double) * 6);
+        return;
+    } else if (b2empty) {
+        memcpy(boundsDest, bounds1, sizeof(double) * 6);
+        return;
+    }
+
+    for (int i = 0; i < 6; i++) {
+        if (i % 2 == 0)
+            boundsDest[i] = min2(bounds1[i], bounds2[i]);
+        else
+            boundsDest[i] = max2(bounds1[i], bounds2[i]);
+    }
+}
+
+/**
+ * \brief Collect bounds of all graphics objects
+ *
+ * \param[out] bounds Bounds of all scene objects
+ * \param[in] onlyVisible Only collect bounds of visible objects
+ */
+void Renderer::collectBounds(double *bounds, bool onlyVisible)
+{
+    bounds[0] = DBL_MAX;
+    bounds[1] = -DBL_MAX;
+    bounds[2] = DBL_MAX;
+    bounds[3] = -DBL_MAX;
+    bounds[4] = DBL_MAX;
+    bounds[5] = -DBL_MAX;
+
+    mergeGraphicsObjectBounds<Arc>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Arrow>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Box>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Cone>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Contour2D>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Contour3D>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Cutplane>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Cylinder>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Disk>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Glyphs>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Group>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<HeightMap>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Image>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<ImageCutplane>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<LIC>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Line>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Molecule>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Outline>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Parallelepiped>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<PolyData>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Polygon>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<PseudoColor>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Sphere>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Streamlines>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Text3D>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Volume>(bounds, onlyVisible);
+    mergeGraphicsObjectBounds<Warp>(bounds, onlyVisible);
+
+    for (int i = 0; i < 6; i += 2) {
+        if (bounds[i+1] < bounds[i]) {
+            bounds[i] = -0.5;
+            bounds[i+1] = 0.5;
+        }
+    }
+
+    int numDims = 0;
+    if (bounds[0] != bounds[1])
+        numDims++;
+    if (bounds[2] != bounds[3])
+        numDims++;
+    if (bounds[4] != bounds[5])
+        numDims++;
+
+    if (numDims == 0) {
+        bounds[0] -= .5;
+        bounds[1] += .5;
+        bounds[2] -= .5;
+        bounds[3] += .5;
+    }
+
+    TRACE("Bounds: %g %g %g %g %g %g",
+          bounds[0],
+          bounds[1],
+          bounds[2],
+          bounds[3],
+          bounds[4],
+          bounds[5]);
+}
+
+/**
+ * \brief Collect bounds of all graphics objects
+ *
+ * \param[out] bounds Bounds of all scene objects
+ * \param[in] onlyVisible Only collect bounds of visible objects
+ */
+void Renderer::collectUnscaledBounds(double *bounds, bool onlyVisible)
+{
+    bounds[0] = DBL_MAX;
+    bounds[1] = -DBL_MAX;
+    bounds[2] = DBL_MAX;
+    bounds[3] = -DBL_MAX;
+    bounds[4] = DBL_MAX;
+    bounds[5] = -DBL_MAX;
+
+    mergeGraphicsObjectUnscaledBounds<Arc>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Arrow>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Box>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Cone>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Contour2D>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Contour3D>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Cutplane>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Cylinder>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Disk>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Glyphs>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<HeightMap>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Image>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<ImageCutplane>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<LIC>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Line>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Molecule>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Outline>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<PolyData>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Parallelepiped>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Polygon>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<PseudoColor>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Sphere>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Streamlines>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Text3D>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Volume>(bounds, onlyVisible);
+    mergeGraphicsObjectUnscaledBounds<Warp>(bounds, onlyVisible);
+
+    for (int i = 0; i < 6; i += 2) {
+        if (bounds[i+1] < bounds[i]) {
+            bounds[i] = -0.5;
+            bounds[i+1] = 0.5;
+        }
+    }
+
+    int numDims = 0;
+    if (bounds[0] != bounds[1])
+        numDims++;
+    if (bounds[2] != bounds[3])
+        numDims++;
+    if (bounds[4] != bounds[5])
+        numDims++;
+
+    if (numDims == 0) {
+        bounds[0] -= .5;
+        bounds[1] += .5;
+        bounds[2] -= .5;
+        bounds[3] += .5;
+    }
+
+    TRACE("Bounds: %g %g %g %g %g %g",
+          bounds[0],
+          bounds[1],
+          bounds[2],
+          bounds[3],
+          bounds[4],
+          bounds[5]);
+}
+
+/**
+ * \brief Update data ranges for color-mapping and contours
+ */
+void Renderer::updateFieldRanges()
+{
+    collectDataRanges();
+
+    updateGraphicsObjectFieldRanges<Contour2D>();
+    updateGraphicsObjectFieldRanges<Contour3D>();
+    updateGraphicsObjectFieldRanges<Cutplane>();
+    updateGraphicsObjectFieldRanges<Glyphs>();
+    updateGraphicsObjectFieldRanges<HeightMap>();
+    updateGraphicsObjectFieldRanges<Image>();
+    updateGraphicsObjectFieldRanges<ImageCutplane>();
+    updateGraphicsObjectFieldRanges<LIC>();
+    updateGraphicsObjectFieldRanges<Molecule>();
+    updateGraphicsObjectFieldRanges<PolyData>();
+    updateGraphicsObjectFieldRanges<PseudoColor>();
+    updateGraphicsObjectFieldRanges<Streamlines>();
+    updateGraphicsObjectFieldRanges<Volume>();
+    updateGraphicsObjectFieldRanges<Warp>();
+}
+
+/**
+ * \brief Collect cumulative data range of all DataSets
+ *
+ * \param[out] range Data range of all DataSets
+ * \param[in] name Field name
+ * \param[in] type Attribute type: e.g. POINT_DATA, CELL_DATA
+ * \param[in] component Array component or -1 for magnitude
+ * \param[in] onlyVisible Only collect range of visible DataSets
+ */
+void Renderer::collectDataRanges(double *range, const char *name,
+                                 DataSet::DataAttributeType type,
+                                 int component, bool onlyVisible)
+{
+    range[0] = DBL_MAX;
+    range[1] = -DBL_MAX;
+
+    for (DataSetHashmap::iterator itr = _dataSets.begin();
+         itr != _dataSets.end(); ++itr) {
+        if (!onlyVisible || itr->second->getVisibility()) {
+            double r[2];
+            r[0] = DBL_MAX;
+            r[1] = -DBL_MAX;
+            itr->second->getDataRange(r, name, type, component);
+            range[0] = min2(range[0], r[0]);
+            range[1] = max2(range[1], r[1]);
+        }
+    }
+    if (range[0] == DBL_MAX)
+        range[0] = 0;
+    if (range[1] == -DBL_MAX)
+        range[1] = 1;
+    
+    TRACE("n:'%s' t:%d c:%d [%g,%g]", name, type, component,
+          range[0], range[1]);
+}
+
+/**
+ * \brief Clear field range hashtables and free memory
+ */
+void Renderer::clearFieldRanges()
+{
+    TRACE("Deleting Field Ranges");
+    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
+         itr != _scalarPointDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _scalarPointDataRange.clear();
+    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
+         itr != _scalarCellDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _scalarCellDataRange.clear();
+    for (FieldRangeHashmap::iterator itr = _scalarFieldDataRange.begin();
+         itr != _scalarFieldDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _scalarFieldDataRange.clear();
+    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
+         itr != _vectorPointDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _vectorPointDataRange.clear();
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
+             itr != _vectorCompPointDataRange[i].end(); ++itr) {
+            delete [] itr->second;
+        }
+        _vectorCompPointDataRange[i].clear();
+    }
+    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
+         itr != _vectorCellDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _vectorCellDataRange.clear();
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
+             itr != _vectorCompCellDataRange[i].end(); ++itr) {
+            delete [] itr->second;
+        }
+        _vectorCompCellDataRange[i].clear();
+    }
+    for (FieldRangeHashmap::iterator itr = _vectorFieldDataRange.begin();
+         itr != _vectorFieldDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _vectorFieldDataRange.clear();
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _vectorCompFieldDataRange[i].begin();
+             itr != _vectorCompFieldDataRange[i].end(); ++itr) {
+            delete [] itr->second;
+        }
+        _vectorCompFieldDataRange[i].clear();
+    }
+}
+
+/**
+ * \brief Clear user-defined field range hashtables and free memory
+ */
+void Renderer::clearUserFieldRanges()
+{
+    TRACE("Deleting User Field Ranges");
+    for (FieldRangeHashmap::iterator itr = _userScalarPointDataRange.begin();
+         itr != _userScalarPointDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _userScalarPointDataRange.clear();
+    for (FieldRangeHashmap::iterator itr = _userScalarCellDataRange.begin();
+         itr != _userScalarCellDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _userScalarCellDataRange.clear();
+    for (FieldRangeHashmap::iterator itr = _userScalarFieldDataRange.begin();
+         itr != _userScalarFieldDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _userScalarFieldDataRange.clear();
+    for (FieldRangeHashmap::iterator itr = _userVectorPointDataRange.begin();
+         itr != _userVectorPointDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _userVectorPointDataRange.clear();
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _userVectorCompPointDataRange[i].begin();
+             itr != _userVectorCompPointDataRange[i].end(); ++itr) {
+            delete [] itr->second;
+        }
+        _userVectorCompPointDataRange[i].clear();
+    }
+    for (FieldRangeHashmap::iterator itr = _userVectorCellDataRange.begin();
+         itr != _userVectorCellDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _userVectorCellDataRange.clear();
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _userVectorCompCellDataRange[i].begin();
+             itr != _userVectorCompCellDataRange[i].end(); ++itr) {
+            delete [] itr->second;
+        }
+        _userVectorCompCellDataRange[i].clear();
+    }
+    for (FieldRangeHashmap::iterator itr = _userVectorFieldDataRange.begin();
+         itr != _userVectorFieldDataRange.end(); ++itr) {
+        delete [] itr->second;
+    }
+    _userVectorFieldDataRange.clear();
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _userVectorCompFieldDataRange[i].begin();
+             itr != _userVectorCompFieldDataRange[i].end(); ++itr) {
+            delete [] itr->second;
+        }
+        _userVectorCompFieldDataRange[i].clear();
+    }
+}
+
+/**
+ * \brief Set up hashtables for min/max values of all fields from loaded 
+ * datasets
+ *
+ * Note that this method does not set the ranges, it just creates the table 
+ * entries
+ */
+void Renderer::initFieldRanges()
+{
+    clearFieldRanges();
+
+    for (DataSetHashmap::iterator itr = _dataSets.begin();
+         itr != _dataSets.end(); ++itr) {
+        DataSet *ds = itr->second;
+        std::vector<std::string> names;
+        ds->getFieldNames(names, DataSet::POINT_DATA, 1);
+        for (std::vector<std::string>::iterator itr = names.begin();
+             itr != names.end(); ++itr) {
+            FieldRangeHashmap::iterator fritr = 
+                _scalarPointDataRange.find(*itr);
+            if (fritr == _scalarPointDataRange.end()) {
+                _scalarPointDataRange[*itr] = new double[2];
+                _scalarPointDataRange[*itr][0] = 0;
+                _scalarPointDataRange[*itr][1] = 1;
+            }
+        }
+        names.clear();
+        ds->getFieldNames(names, DataSet::CELL_DATA, 1);
+        for (std::vector<std::string>::iterator itr = names.begin();
+             itr != names.end(); ++itr) {
+            FieldRangeHashmap::iterator fritr = 
+                _scalarCellDataRange.find(*itr);
+            if (fritr == _scalarCellDataRange.end()) {
+                _scalarCellDataRange[*itr] = new double[2];
+                _scalarCellDataRange[*itr][0] = 0;
+                _scalarCellDataRange[*itr][1] = 1;
+            }
+        }
+        names.clear();
+        ds->getFieldNames(names, DataSet::FIELD_DATA, 1);
+        for (std::vector<std::string>::iterator itr = names.begin();
+             itr != names.end(); ++itr) {
+            FieldRangeHashmap::iterator fritr = 
+                _scalarFieldDataRange.find(*itr);
+            if (fritr == _scalarFieldDataRange.end()) {
+                _scalarFieldDataRange[*itr] = new double[2];
+                _scalarFieldDataRange[*itr][0] = 0;
+                _scalarFieldDataRange[*itr][1] = 1;
+            }
+        }
+        names.clear();
+        ds->getFieldNames(names, DataSet::POINT_DATA, 3);
+        for (std::vector<std::string>::iterator itr = names.begin();
+             itr != names.end(); ++itr) {
+            FieldRangeHashmap::iterator fritr = 
+                _vectorPointDataRange.find(*itr);
+            if (fritr == _vectorPointDataRange.end()) {
+                _vectorPointDataRange[*itr] = new double[2];
+                _vectorPointDataRange[*itr][0] = 0;
+                _vectorPointDataRange[*itr][1] = 1;
+            }
+            for (int i = 0; i < 3; i++) {
+                fritr = _vectorCompPointDataRange[i].find(*itr);
+                if (fritr == _vectorCompPointDataRange[i].end()) {
+                    _vectorCompPointDataRange[i][*itr] = new double[2];
+                    _vectorCompPointDataRange[i][*itr][0] = 0;
+                    _vectorCompPointDataRange[i][*itr][1] = 1;
+                }
+            }
+        }
+        names.clear();
+        ds->getFieldNames(names, DataSet::CELL_DATA, 3);
+        for (std::vector<std::string>::iterator itr = names.begin();
+             itr != names.end(); ++itr) {
+            FieldRangeHashmap::iterator fritr = 
+                _vectorCellDataRange.find(*itr);
+            if (fritr == _vectorCellDataRange.end()) {
+                _vectorCellDataRange[*itr] = new double[2];
+                _vectorCellDataRange[*itr][0] = 0;
+                _vectorCellDataRange[*itr][1] = 1;
+            }
+            for (int i = 0; i < 3; i++) {
+                fritr = _vectorCompCellDataRange[i].find(*itr);
+                if (fritr == _vectorCompCellDataRange[i].end()) {
+                    _vectorCompCellDataRange[i][*itr] = new double[2];
+                    _vectorCompCellDataRange[i][*itr][0] = 0;
+                    _vectorCompCellDataRange[i][*itr][1] = 1;
+                }
+            }
+        }
+        names.clear();
+        ds->getFieldNames(names, DataSet::FIELD_DATA, 3);
+        for (std::vector<std::string>::iterator itr = names.begin();
+             itr != names.end(); ++itr) {
+            FieldRangeHashmap::iterator fritr = 
+                _vectorFieldDataRange.find(*itr);
+            if (fritr == _vectorFieldDataRange.end()) {
+                _vectorFieldDataRange[*itr] = new double[2];
+                _vectorFieldDataRange[*itr][0] = 0;
+                _vectorFieldDataRange[*itr][1] = 1;
+            }
+            for (int i = 0; i < 3; i++) {
+                fritr = _vectorCompFieldDataRange[i].find(*itr);
+                if (fritr == _vectorCompFieldDataRange[i].end()) {
+                    _vectorCompFieldDataRange[i][*itr] = new double[2];
+                    _vectorCompFieldDataRange[i][*itr][0] = 0;
+                    _vectorCompFieldDataRange[i][*itr][1] = 1;
+                }
+            }
+        }
+    }
+}
+
+/**
+ * \brief Returns boolean flag indicating if cumulative data ranges 
+ * should be used
+ */
+bool Renderer::getUseCumulativeRange()
+{
+    return _useCumulativeRange;
+}
+
+/**
+ * \brief Set explicit range to use for mapping fields
+ */
+bool Renderer::setCumulativeDataRange(double *range, const char *name,
+                                      DataSet::DataAttributeType type,
+                                      int numComponents,
+                                      int component)
+{
+    if (range == NULL || name == NULL)
+        return false;
+
+    _useCumulativeRange = true;
+    bool found = false;
+
+    switch (type) {
+    case DataSet::POINT_DATA: {
+        FieldRangeHashmap::iterator itr;
+        if (numComponents == 1) {
+            itr = _userScalarPointDataRange.find(name);
+            if (itr == _userScalarPointDataRange.end()) {
+                _userScalarPointDataRange[name] = new double[2];
+                memcpy(_userScalarPointDataRange[name], range, sizeof(double)*2);
+            } else {
+                found = true;
+                memcpy(itr->second, range, sizeof(double)*2);
+            }
+        } else if (numComponents == 3) {
+            if (component == -1) {
+                itr = _userVectorPointDataRange.find(name);
+                if (itr == _userVectorPointDataRange.end()) {
+                    _userVectorPointDataRange[name] = new double[2];
+                    memcpy(_userVectorPointDataRange[name], range, sizeof(double)*2);
+                } else {
+                    found = true;
+                    memcpy(itr->second, range, sizeof(double)*2);
+                }
+            } else if (component >= 0 && component <= 3) {
+                itr = _userVectorCompPointDataRange[component].find(name);
+                if (itr == _userVectorCompPointDataRange[component].end()) {
+                    _userVectorCompPointDataRange[component][name] = new double[2];
+                    memcpy(_userVectorCompPointDataRange[component][name], range, sizeof(double)*2);
+                } else {
+                    found = true;
+                    memcpy(itr->second, range, sizeof(double)*2);
+                }
+            }
+        }
+    }
+        break;
+    case DataSet::CELL_DATA: {
+        FieldRangeHashmap::iterator itr;
+        if (numComponents == 1) {
+            itr = _userScalarCellDataRange.find(name);
+            if (itr == _userScalarCellDataRange.end()) {
+                _userScalarCellDataRange[name] = new double[2];
+                memcpy(_userScalarCellDataRange[name], range, sizeof(double)*2);
+            } else {
+                found = true;
+                memcpy(itr->second, range, sizeof(double)*2);
+            }
+        } else if (numComponents == 3) {
+            if (component == -1) {
+                itr = _userVectorCellDataRange.find(name);
+                if (itr == _userVectorCellDataRange.end()) {
+                    _userVectorCellDataRange[name] = new double[2];
+                    memcpy(_userVectorCellDataRange[name], range, sizeof(double)*2);
+                } else {
+                    found = true;
+                    memcpy(itr->second, range, sizeof(double)*2);
+                }
+            } else if (component >= 0 && component <= 3) {
+                itr = _userVectorCompCellDataRange[component].find(name);
+                if (itr == _userVectorCompCellDataRange[component].end()) {
+                    _userVectorCompCellDataRange[component][name] = new double[2];
+                    memcpy(_userVectorCompCellDataRange[component][name], range, sizeof(double)*2);
+                } else {
+                    found = true;
+                    memcpy(itr->second, range, sizeof(double)*2);
+                }
+            }
+        }
+    }
+        break;
+    case DataSet::FIELD_DATA: {
+        FieldRangeHashmap::iterator itr;
+        if (numComponents == 1) {
+            itr = _userScalarFieldDataRange.find(name);
+            if (itr == _userScalarFieldDataRange.end()) {
+                _userScalarFieldDataRange[name] = new double[2];
+                memcpy(_userScalarFieldDataRange[name], range, sizeof(double)*2);
+            } else {
+                found = true;
+                memcpy(itr->second, range, sizeof(double)*2);
+            }
+        } else if (numComponents == 3) {
+            if (component == -1) {
+                itr = _userVectorFieldDataRange.find(name);
+                if (itr == _userVectorFieldDataRange.end()) {
+                    _userVectorFieldDataRange[name] = new double[2];
+                    memcpy(_userVectorFieldDataRange[name], range, sizeof(double)*2);
+                } else {
+                    found = true;
+                    memcpy(itr->second, range, sizeof(double)*2);
+                }
+            } else if (component >= 0 && component <= 3) {
+                itr = _userVectorCompFieldDataRange[component].find(name);
+                if (itr == _userVectorCompFieldDataRange[component].end()) {
+                    _userVectorCompFieldDataRange[component][name] = new double[2];
+                    memcpy(_userVectorCompFieldDataRange[component][name], range, sizeof(double)*2);
+                } else {
+                    found = true;
+                    memcpy(itr->second, range, sizeof(double)*2);
+                }
+            }
+        }
+    }
+        break;
+    default:
+        ERROR("Bad Field Type");
+    }
+
+    // Notify graphics objects of new ranges
+    updateFieldRanges();
+    // Bounds may have changed
+    sceneBoundsChanged();
+    _needsRedraw = true;
+
+    TRACE("Field: %s found: %d range: %g %g", name, (found ? 1 : 0), range[0], range[1]);
+    return found;
+}
+
+/**
+ * \brief Get the cumulative range across all DataSets for a point
+ * data field if it exists, otherwise a cell data field if it exists,
+ * otherwise a field data field if it exists
+ *
+ * \param[out] range Pointer to an array of 2 doubles
+ * \param[in] name Field name
+ * \param[in] numComponents Number of components in field
+ * \param[in] component Index of component or -1 for magnitude/scalar
+ * \return boolean indicating if field was found
+ */
+bool Renderer::getCumulativeDataRange(double *range, const char *name,
+                                      int numComponents,
+                                      int component)
+{
+    bool ret;
+    if ((ret = getCumulativeDataRange(range, name, DataSet::POINT_DATA,
+                                      numComponents, component))) {
+        ; // Found point data
+    } else if ((ret = getCumulativeDataRange(range, name, DataSet::CELL_DATA,
+                                             numComponents, component))) {
+        ; // Found cell data
+        
+    } else {
+        ret = getCumulativeDataRange(range, name, DataSet::FIELD_DATA,
+                                     numComponents, component);
+    }
+    return ret;
+}
+
+/**
+ * \brief Get the cumulative range across all DataSets for a field
+ *
+ * \param[out] range Pointer to an array of 2 doubles
+ * \param[in] name Field name
+ * \param[in] type DataAttributeType of field
+ * \param[in] numComponents Number of components in field
+ * \param[in] component Index of component or -1 for magnitude/scalar
+ * \return boolean indicating if field was found
+ */
+bool Renderer::getCumulativeDataRange(double *range, const char *name,
+                                      DataSet::DataAttributeType type,
+                                      int numComponents,
+                                      int component)
+{
+    if (range == NULL || name == NULL)
+        return false;
+
+    switch (type) {
+    case DataSet::POINT_DATA: {
+        FieldRangeHashmap::iterator itr;
+        if (numComponents == 1) {
+            itr = _userScalarPointDataRange.find(name);
+            if (itr == _userScalarPointDataRange.end()) {
+                itr = _scalarPointDataRange.find(name);
+                if (itr == _scalarPointDataRange.end()) {
+                    return false;
+                }
+            }
+            memcpy(range, itr->second, sizeof(double)*2);
+            return true;
+        } else if (numComponents == 3) {
+            if (component == -1) {
+                itr = _userVectorPointDataRange.find(name);
+                if (itr == _userVectorPointDataRange.end()) {
+                    itr = _vectorPointDataRange.find(name);
+                    if (itr == _vectorPointDataRange.end()) {
+                        return false;
+                    }
+                }
+                memcpy(range, itr->second, sizeof(double)*2);
+                return true;
+            } else if (component >= 0 && component <= 3) {
+                itr = _userVectorCompPointDataRange[component].find(name);
+                if (itr == _userVectorCompPointDataRange[component].end()) {
+                    itr = _vectorCompPointDataRange[component].find(name);
+                    if (itr == _vectorCompPointDataRange[component].end()) {
+                        return false;
+                    }
+                }
+                memcpy(range, itr->second, sizeof(double)*2);
+                return true;
+            }
+        }
+    }
+        break;
+    case DataSet::CELL_DATA: {
+        FieldRangeHashmap::iterator itr;
+        if (numComponents == 1) {
+            itr = _userScalarCellDataRange.find(name);
+            if (itr == _userScalarCellDataRange.end()) {
+                itr = _scalarCellDataRange.find(name);
+                if (itr == _scalarCellDataRange.end()) {
+                    return false;
+                }
+            }
+            memcpy(range, itr->second, sizeof(double)*2);
+            return true;
+        } else if (numComponents == 3) {
+            if (component == -1) {
+                itr = _userVectorCellDataRange.find(name);
+                if (itr == _userVectorCellDataRange.end()) {
+                    itr = _vectorCellDataRange.find(name);
+                    if (itr == _vectorCellDataRange.end()) {
+                        return false;
+                    }
+                }
+                memcpy(range, itr->second, sizeof(double)*2);
+                return true;
+            } else if (component >= 0 && component <= 3) {
+                itr = _userVectorCompCellDataRange[component].find(name);
+                if (itr == _userVectorCompCellDataRange[component].end()) {
+                    itr = _vectorCompCellDataRange[component].find(name);
+                    if (itr == _vectorCompCellDataRange[component].end()) {
+                        return false;
+                    }
+                }
+                memcpy(range, itr->second, sizeof(double)*2);
+                return true;
+            }
+        }
+    }
+        break;
+    case DataSet::FIELD_DATA: {
+        FieldRangeHashmap::iterator itr;
+        if (numComponents == 1) {
+            itr = _userScalarFieldDataRange.find(name);
+            if (itr == _userScalarFieldDataRange.end()) {
+                itr = _scalarFieldDataRange.find(name);
+                if (itr == _scalarFieldDataRange.end()) {
+                    return false;
+                }
+            }
+            memcpy(range, itr->second, sizeof(double)*2);
+            return true;
+        } else if (numComponents == 3) {
+            if (component == -1) {
+                itr = _userVectorFieldDataRange.find(name);
+                if (itr == _userVectorFieldDataRange.end()) {
+                    itr = _vectorFieldDataRange.find(name);
+                    if (itr == _vectorFieldDataRange.end()) {
+                        return false;
+                    }
+                }
+                memcpy(range, itr->second, sizeof(double)*2);
+                return true;
+            } else if (component >= 0 && component <= 3) {
+                itr = _userVectorCompFieldDataRange[component].find(name);
+                if (itr == _userVectorCompFieldDataRange[component].end()) {
+                    itr = _vectorCompFieldDataRange[component].find(name);
+                    if (itr == _vectorCompFieldDataRange[component].end()) {
+                        return false;
+                    }
+                }
+                memcpy(range, itr->second, sizeof(double)*2);
+                return true;
+            }
+        }
+    }
+        break;
+    default:
+        break;
+    }
+    return false;
+}
+
+void Renderer::collectDataRanges()
+{
+    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
+         itr != _scalarPointDataRange.end(); ++itr) {
+        collectDataRanges(itr->second, itr->first.c_str(),
+                          DataSet::POINT_DATA, -1,
+                          _cumulativeRangeOnlyVisible);
+    }
+    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
+         itr != _scalarCellDataRange.end(); ++itr) {
+        collectDataRanges(itr->second, itr->first.c_str(),
+                          DataSet::CELL_DATA, -1,
+                          _cumulativeRangeOnlyVisible);
+    }
+    for (FieldRangeHashmap::iterator itr = _scalarFieldDataRange.begin();
+         itr != _scalarFieldDataRange.end(); ++itr) {
+        collectDataRanges(itr->second, itr->first.c_str(),
+                          DataSet::FIELD_DATA, -1,
+                          _cumulativeRangeOnlyVisible);
+    }
+    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
+         itr != _vectorPointDataRange.end(); ++itr) {
+        collectDataRanges(itr->second, itr->first.c_str(),
+                          DataSet::POINT_DATA, -1,
+                          _cumulativeRangeOnlyVisible);
+    }
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
+             itr != _vectorCompPointDataRange[i].end(); ++itr) {
+            collectDataRanges(itr->second, itr->first.c_str(),
+                              DataSet::POINT_DATA, i,
+                              _cumulativeRangeOnlyVisible);
+        }
+    }
+    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
+         itr != _vectorCellDataRange.end(); ++itr) {
+        collectDataRanges(itr->second, itr->first.c_str(),
+                          DataSet::CELL_DATA, -1,
+                          _cumulativeRangeOnlyVisible);
+    }
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
+             itr != _vectorCompCellDataRange[i].end(); ++itr) {
+            collectDataRanges(itr->second, itr->first.c_str(),
+                              DataSet::CELL_DATA, i,
+                              _cumulativeRangeOnlyVisible);
+        }
+    }
+    for (FieldRangeHashmap::iterator itr = _vectorFieldDataRange.begin();
+         itr != _vectorFieldDataRange.end(); ++itr) {
+        collectDataRanges(itr->second, itr->first.c_str(),
+                          DataSet::FIELD_DATA, -1,
+                          _cumulativeRangeOnlyVisible);
+    }
+    for (int i = 0; i < 3; i++) {
+        for (FieldRangeHashmap::iterator itr = _vectorCompFieldDataRange[i].begin();
+             itr != _vectorCompFieldDataRange[i].end(); ++itr) {
+            collectDataRanges(itr->second, itr->first.c_str(),
+                              DataSet::FIELD_DATA, i,
+                              _cumulativeRangeOnlyVisible);
+        }
+    }
+}
+
+/**
+ * \brief Determines if AABB lies in a principal axis plane
+ * and if so, returns the plane normal
+ */
+bool Renderer::is2D(const double bounds[6],
+                    PrincipalPlane *plane,
+                    double *offset) const
+{
+    if (bounds[4] == bounds[5]) {
+        // Z = 0, XY plane
+        if (plane)
+            *plane = PLANE_XY;
+        if (offset)
+            *offset = bounds[4];
+        return true;
+    } else if (bounds[0] == bounds[1]) {
+        // X = 0, ZY plane
+        if (plane)
+            *plane = PLANE_ZY;
+        if (offset)
+            *offset = bounds[0];
+        return true;
+    } else if (bounds[2] == bounds[3]) {
+        // Y = 0, XZ plane
+        if (plane)
+            *plane = PLANE_XZ;
+        if (offset)
+            *offset = bounds[2];
+        return true;
+    }
+    *plane = PLANE_XY;
+    *offset = 0; //bounds[4] + (bounds[5] - bounds[4])/2.0;
+    return false;
+}
+
+int Renderer::addLight(float pos[3])
+{
+    vtkSmartPointer<vtkLight> light = vtkSmartPointer<vtkLight>::New();
+    light->SetLightTypeToCameraLight();
+    light->SetPosition(pos[0], pos[1], pos[2]);
+    light->SetFocalPoint(0, 0, 0);
+    light->PositionalOff();
+    _renderer->AddLight(light);
+    _needsRedraw = true;
+    return (_renderer->GetLights()->GetNumberOfItems()-1);
+}
+
+vtkLight *Renderer::getLight(int lightIdx)
+{
+    vtkLightCollection *lights = _renderer->GetLights();
+    if (lights->GetNumberOfItems() < lightIdx+1)
+        return NULL;
+    lights->InitTraversal();
+    vtkLight *light = NULL;
+    int i = 0;
+    do {
+        light = lights->GetNextItem();
+    } while (i++ < lightIdx);
+    return light;
+}
+
+void Renderer::setLightSwitch(int lightIdx, bool state)
+{
+    vtkLight *light = getLight(lightIdx);
+    if (light == NULL) {
+        ERROR("Unknown light %d", lightIdx);
+        return;
+    }
+    light->SetSwitch((state ? 1 : 0));
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Initialize the camera zoom region to include the bounding volume given
+ */
+void Renderer::initCamera(bool initCameraMode)
+{
+#ifdef WANT_TRACE
+    switch (_cameraMode) {
+    case IMAGE:
+        TRACE("Image camera");
+        break;
+    case ORTHO:
+        TRACE("Ortho camera");
+        break;
+    case PERSPECTIVE:
+        TRACE("Perspective camera");
+        break;
+    default:
+        TRACE("Unknown camera mode");
+    }
+#endif
+    // Clear user requested zoom region
+    _userImgWorldOrigin[0] = 0;
+    _userImgWorldOrigin[1] = 0;
+    _userImgWorldDims[0] = -1;
+    _userImgWorldDims[1] = -1;
+
+    double bounds[6];
+    collectBounds(bounds, _computeOnlyVisibleBounds);
+    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
+    if (twod) {
+        if (initCameraMode) {
+            TRACE("Changing camera mode to image");
+            _cameraMode = IMAGE;
+        }
+        if (_imgCameraPlane == PLANE_ZY) {
+            _imgWorldOrigin[0] = bounds[4];
+            _imgWorldOrigin[1] = bounds[2];
+            _imgWorldDims[0] = bounds[5] - bounds[4];
+            _imgWorldDims[1] = bounds[3] - bounds[2];
+        } else if (_imgCameraPlane == PLANE_XZ) {
+            _imgWorldOrigin[0] = bounds[0];
+            _imgWorldOrigin[1] = bounds[4];
+            _imgWorldDims[0] = bounds[1] - bounds[0];
+            _imgWorldDims[1] = bounds[5] - bounds[4];
+        } else {
+            _imgWorldOrigin[0] = bounds[0];
+            _imgWorldOrigin[1] = bounds[2];
+            _imgWorldDims[0] = bounds[1] - bounds[0];
+            _imgWorldDims[1] = bounds[3] - bounds[2];
+        }
+    } else {
+        _imgWorldOrigin[0] = bounds[0];
+        _imgWorldOrigin[1] = bounds[2];
+        _imgWorldDims[0] = bounds[1] - bounds[0];
+        _imgWorldDims[1] = bounds[3] - bounds[2];
+    }
+
+    _cameraPan[0] = 0;
+    _cameraPan[1] = 0;
+    _cameraZoomRatio = 1;
+
+    switch (_cameraMode) {
+    case IMAGE:
+        //_renderer->ResetCamera(bounds);
+        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
+                             _imgWorldDims[0], _imgWorldDims[1]);
+        resetAxes(bounds);
+        break;
+    case ORTHO:
+        _renderer->GetActiveCamera()->ParallelProjectionOn();
+        resetAxes(bounds);
+        //_renderer->ResetCamera(bounds);
+        resetVtkCamera();
+        //computeScreenWorldCoords();
+        break;
+    case PERSPECTIVE:
+        _renderer->GetActiveCamera()->ParallelProjectionOff();
+        resetAxes(bounds);
+        //_renderer->ResetCamera(bounds);
+        resetVtkCamera();
+        //computeScreenWorldCoords();
+        break;
+    default:
+        ERROR("Unknown camera mode");
+    }
+
+#ifdef WANT_TRACE
+    printCameraInfo(_renderer->GetActiveCamera());
+#endif
+}
+
+#if 0
+/**
+ * \brief Print debugging info about a vtkCamera
+ */
+void Renderer::printCameraInfo(vtkCamera *camera)
+{
+    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g",
+          camera->GetParallelScale(),
+          camera->GetViewAngle(),
+          camera->GetDistance(),
+          camera->GetPosition()[0],
+          camera->GetPosition()[1],
+          camera->GetPosition()[2], 
+          camera->GetFocalPoint()[0],
+          camera->GetFocalPoint()[1],
+          camera->GetFocalPoint()[2],
+          camera->GetViewUp()[0],
+          camera->GetViewUp()[1],
+          camera->GetViewUp()[2],
+          camera->GetClippingRange()[0],
+          camera->GetClippingRange()[1]);
+}
+#endif
+
+/**
+ * \brief Set the RGB background color to render into the image
+ */
+void Renderer::setBackgroundColor(float color[3])
+{
+    _bgColor[0] = color[0];
+    _bgColor[1] = color[1];
+    _bgColor[2] = color[2];
+    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the opacity of the specified DataSet's associated graphics objects
+ */
+void Renderer::setDataSetOpacity(const DataSetId& id, double opacity)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+        if (itr == _dataSets.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOpacity(opacity);
+    } while (doAll && ++itr != _dataSets.end());
+
+    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
+        setGraphicsObjectOpacity<Contour2D>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
+        setGraphicsObjectOpacity<Contour3D>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
+        setGraphicsObjectOpacity<Cutplane>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
+        setGraphicsObjectOpacity<Glyphs>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
+        setGraphicsObjectOpacity<HeightMap>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Image>(id) != NULL)
+        setGraphicsObjectOpacity<Image>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<ImageCutplane>(id) != NULL)
+        setGraphicsObjectOpacity<ImageCutplane>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
+        setGraphicsObjectOpacity<LIC>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
+        setGraphicsObjectOpacity<Molecule>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
+        setGraphicsObjectOpacity<Outline>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
+        setGraphicsObjectOpacity<PolyData>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
+        setGraphicsObjectOpacity<PseudoColor>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
+        setGraphicsObjectOpacity<Streamlines>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
+        setGraphicsObjectOpacity<Volume>(id, opacity);
+    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
+        setGraphicsObjectOpacity<Warp>(id, opacity);
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
+ */
+void Renderer::setDataSetVisibility(const DataSetId& id, bool state)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+        doAll = true;
+        if (itr == _dataSets.end())
+            return;
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setVisibility(state);
+    } while (doAll && ++itr != _dataSets.end());
+
+    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
+        setGraphicsObjectVisibility<Contour2D>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
+        setGraphicsObjectVisibility<Contour3D>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
+        setGraphicsObjectVisibility<Cutplane>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
+        setGraphicsObjectVisibility<Glyphs>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
+        setGraphicsObjectVisibility<HeightMap>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Image>(id) != NULL)
+        setGraphicsObjectVisibility<Image>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<ImageCutplane>(id) != NULL)
+        setGraphicsObjectVisibility<ImageCutplane>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
+        setGraphicsObjectVisibility<LIC>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
+        setGraphicsObjectVisibility<Molecule>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
+        setGraphicsObjectVisibility<Outline>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
+        setGraphicsObjectVisibility<PolyData>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
+        setGraphicsObjectVisibility<PseudoColor>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
+        setGraphicsObjectVisibility<Streamlines>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
+        setGraphicsObjectVisibility<Volume>(id, state);
+    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
+        setGraphicsObjectVisibility<Warp>(id, state);
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set a user clipping plane
+ *
+ * TODO: Fix clip plane positions after a change in actor bounds
+ */
+void Renderer::setClipPlane(Axis axis, double ratio, int direction)
+{
+    double bounds[6];
+    collectBounds(bounds, _computeOnlyVisibleBounds);
+
+    switch (axis) {
+    case X_AXIS:
+        if (direction > 0) {
+            if (ratio > 0.0) {
+                if (_userClipPlanes[0] == NULL) {
+                    _userClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
+                    _userClipPlanes[0]->SetNormal(1, 0, 0);
+                }
+                _userClipPlanes[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
+            } else {
+                _userClipPlanes[0] = NULL;
+            }
+        } else {
+            if (ratio < 1.0) {
+                if (_userClipPlanes[1] == NULL) {
+                    _userClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
+                    _userClipPlanes[1]->SetNormal(-1, 0, 0);
+                }
+                _userClipPlanes[1]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
+            } else {
+                _userClipPlanes[1] = NULL;
+            }
+        }
+        break;
+    case Y_AXIS:
+        if (direction > 0) {
+            if (ratio > 0.0) {
+                if (_userClipPlanes[2] == NULL) {
+                    _userClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
+                    _userClipPlanes[2]->SetNormal(0, 1, 0);
+                }
+                _userClipPlanes[2]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
+            } else {
+                _userClipPlanes[2] = NULL;
+            }
+        } else {
+            if (ratio < 1.0) {
+                if (_userClipPlanes[3] == NULL) {
+                    _userClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
+                    _userClipPlanes[3]->SetNormal(0, -1, 0);
+                }
+                _userClipPlanes[3]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
+            } else {
+                _userClipPlanes[3] = NULL;
+            }
+        }
+        break;
+    case Z_AXIS:
+        if (direction > 0) {
+            if (ratio > 0.0) {
+                if (_userClipPlanes[4] == NULL) {
+                    _userClipPlanes[4] = vtkSmartPointer<vtkPlane>::New();
+                    _userClipPlanes[4]->SetNormal(0, 0, 1);
+                }
+                _userClipPlanes[4]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
+            } else {
+                _userClipPlanes[4] = NULL;
+            }
+        } else {
+            if (ratio < 1.0) {
+                if (_userClipPlanes[5] == NULL) {
+                    _userClipPlanes[5] = vtkSmartPointer<vtkPlane>::New();
+                    _userClipPlanes[5]->SetNormal(0, 0, -1);
+                }
+                _userClipPlanes[5]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
+            } else {
+                _userClipPlanes[5] = NULL;
+            }
+        }
+        break;
+    default:
+        ;
+    }
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set up clipping planes for image camera mode if needed
+ */
+void Renderer::setCameraClippingPlanes()
+{
+    /* XXX: Note that there appears to be a bug with setting the 
+     * clipping plane collection to NULL in the VTK Mappers -- 
+     * the old clip planes are still applied.  The workaround here 
+     * is to keep the PlaneCollection and add/remove the planes 
+     * to/from the PlaneCollection as needed.
+     */
+    if (_cameraMode == IMAGE) {
+        if (_activeClipPlanes->GetNumberOfItems() == 0) {
+            for (int i = 0; i < 4; i++)
+                _activeClipPlanes->AddItem(_cameraClipPlanes[i]);
+        }
+    } else {
+        if (_activeClipPlanes->GetNumberOfItems() > 0)
+            _activeClipPlanes->RemoveAllItems();
+        for (int i = 0; i < 6; i++) {
+            if (_userClipPlanes[i] != NULL) {
+                _activeClipPlanes->AddItem(_userClipPlanes[i]);
+            }
+        }
+    }
+
+    /* Ensure all Mappers are using the PlaneCollection
+     * This will not change the state or timestamp of 
+     * Mappers already using the PlaneCollection
+     */
+    setGraphicsObjectClippingPlanes<Arc>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Arrow>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Box>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Cone>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Contour2D>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Contour3D>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Cutplane>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Cylinder>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Disk>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Glyphs>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Group>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<HeightMap>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Image>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<ImageCutplane>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<LIC>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Line>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Molecule>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Outline>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Parallelepiped>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<PolyData>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Polygon>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<PseudoColor>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Sphere>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Streamlines>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Text3D>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Volume>(_activeClipPlanes);
+    setGraphicsObjectClippingPlanes<Warp>(_activeClipPlanes);
+}
+
+/**
+ * \brief Control the use of two sided lighting
+ */
+void Renderer::setUseTwoSidedLighting(bool state)
+{
+    _renderer->SetTwoSidedLighting(state ? 1 : 0);
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Control parameters of depth peeling algorithm
+ * 
+ * \param occlusionRatio define the threshold under which the algorithm 
+ * stops to iterate over peel layers. This is the ratio of the number of 
+ * pixels that have been touched by the last layer over the total number 
+ * of pixels of the viewport area. Initial value is 0.0, meaning rendering 
+ * have to be exact. Greater values may speed-up the rendering with small 
+ * impact on the quality.
+ * \param maxPeels define the maximum number of peeling layers. Initial 
+ * value is 100. A special value of 0 means no maximum limit. It has to be 
+ * a positive value.
+ */
+void Renderer::setDepthPeelingParams(double occlusionRatio, int maxPeels)
+{
+    _renderer->SetOcclusionRatio(occlusionRatio);
+    _renderer->SetMaximumNumberOfPeels(maxPeels);
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Control the use of the depth peeling algorithm for transparency
+ */
+void Renderer::setUseDepthPeeling(bool state)
+{
+    _renderer->SetUseDepthPeeling(state ? 1 : 0);
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Sets flag to trigger rendering next time render() is called
+ */
+void Renderer::eventuallyRender()
+{
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Cause the rendering to render a new image if needed
+ *
+ * The _needsRedraw flag indicates if a state change has occured since
+ * the last rendered frame
+ */
+bool Renderer::render()
+{
+    TRACE("Enter: redraw: %d axesReset: %d cameraReset: %d clippingRangeReset: %d",
+          _needsRedraw ? 1 : 0, _needsAxesReset ? 1 : 0, _needsCameraReset ? 1 : 0, _needsCameraClippingRangeReset ? 1 : 0);
+    if (_needsRedraw) {
+         if (_needsAxesReset) {
+            resetAxes();
+            _needsAxesReset = false;
+        }
+        if (_needsCameraReset) {
+            initCamera();
+            _needsCameraReset = false;
+            _needsCameraClippingRangeReset = false;
+        } else if (_needsCameraClippingRangeReset && _cameraMode != IMAGE) {
+            resetCameraClippingRange();
+            _needsCameraClippingRangeReset = false;
+        }
+        setCameraClippingPlanes();
+        _renderWindow->Render();
+        int *sz = _renderWindow->GetSize();
+        if (sz[0] != _windowWidth || sz[1] != _windowHeight) {
+            ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], _windowWidth, _windowHeight);
+        }
+        _needsRedraw = false;
+        return true;
+    } else
+        return false;
+}
+
+/// Get the pixel width of the render window/image
+int Renderer::getWindowWidth() const
+{
+    return _windowWidth;
+}
+
+/// Get the pixel height of the render window/image
+int Renderer::getWindowHeight() const
+{
+    return _windowHeight;
+}
+
+/**
+ * \brief Read back the rendered framebuffer image
+ */
+void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
+{
+#ifdef RENDER_TARGA
+    _renderWindow->MakeCurrent();
+    // Must clear previous errors first.
+    while (glGetError() != GL_NO_ERROR){
+        ;
+    }
+    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
+    int size = bytesPerPixel * _windowWidth * _windowHeight;
+
+    if (imgData->GetMaxId() + 1 != size)
+    {
+        imgData->SetNumberOfComponents(bytesPerPixel);
+        imgData->SetNumberOfValues(size);
+    }
+    glDisable(GL_TEXTURE_2D);
+    if (_renderWindow->GetDoubleBuffer()) {
+        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetBackLeftBuffer()));
+    } else {
+        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
+    }
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
+#ifdef WANT_TRACE
+    struct timeval t1, t2;
+    glFinish();
+    gettimeofday(&t1, 0);
+#endif
+    if (bytesPerPixel == 4) {
+        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGRA,
+                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
+    } else {
+        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGR,
+                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
+    }
+#ifdef WANT_TRACE
+    gettimeofday(&t2, 0);
+    static unsigned int numFrames = 0;
+    static double accum = 0;
+    numFrames++;
+    accum += MSECS_ELAPSED(t1, t2);
+#endif
+    TRACE("Readback time: %g ms", MSECS_ELAPSED(t1, t2));
+    TRACE("Readback avg: %g ms", accum/numFrames);
+    if (glGetError() != GL_NO_ERROR) {
+        ERROR("glReadPixels");
+    }
+#else
+    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1,
+                                !_renderWindow->GetDoubleBuffer(), imgData);
+#endif
+    TRACE("Image data size: %d", imgData->GetSize());
+}
+
+/**
+ * \brief Get nearest data value given display coordinates x,y
+ *
+ * FIXME: This doesn't work when actors are scaled
+ *
+ * Note: no interpolation is performed on data
+ */
+bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
+{
+    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
+    coord->SetCoordinateSystemToDisplay();
+    coord->SetValue(x, _windowHeight - y, 0);
+    double *worldCoords = coord->GetComputedWorldValue(_renderer);
+
+    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
+          worldCoords[0], 
+          worldCoords[1], 
+          worldCoords[2]);
+
+    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
+}
+
+/**
+ * \brief Get nearest data value given world coordinates x,y,z
+ *
+ * Note: no interpolation is performed on data
+ */
+bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
+{
+    DataSet *ds = getDataSet(id);
+    if (ds == NULL)
+        return false;
+
+    return ds->getScalarValue(x, y, z, value);
+}
+
+/**
+ * \brief Get nearest data value given display coordinates x,y
+ *
+ * Note: no interpolation is performed on data
+ */
+bool Renderer::getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3])
+{
+    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
+    coord->SetCoordinateSystemToDisplay();
+    coord->SetValue(x, _windowHeight - y, 0);
+    double *worldCoords = coord->GetComputedWorldValue(_renderer);
+
+    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
+          worldCoords[0], 
+          worldCoords[1], 
+          worldCoords[2]);
+
+    return getVectorValue(id, worldCoords[0], worldCoords[1], worldCoords[2], vector);
+}
+
+/**
+ * \brief Get nearest data value given world coordinates x,y,z
+ *
+ * Note: no interpolation is performed on data
+ */
+bool Renderer::getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3])
+{
+    DataSet *ds = getDataSet(id);
+    if (ds == NULL)
+        return false;
+
+    return ds->getVectorValue(x, y, z, vector);
+}
Index: vtkvis/tags/1.8.4/Renderer.h
===================================================================
--- vtkvis/tags/1.8.4/Renderer.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Renderer.h	(revision 6184)
@@ -0,0 +1,1182 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_RENDERER_H
+#define VTKVIS_RENDERER_H
+
+#include <string>
+#include <vector>
+#include <tr1/unordered_map>
+#include <typeinfo>
+
+#include <vtkSmartPointer.h>
+#ifdef USE_CUSTOM_AXES
+#include "vtkRpCubeAxesActor.h"
+#else
+#include <vtkCubeAxesActor.h>
+#endif
+#include <vtkAxesActor.h>
+#include <vtkAnnotatedCubeActor.h>
+#include <vtkScalarBarActor.h>
+#include <vtkRenderer.h>
+#include <vtkRenderWindow.h>
+#include <vtkUnsignedCharArray.h>
+
+#include "ColorMap.h"
+#include "Types.h"
+#include "DataSet.h"
+#include "Arc.h"
+#include "Arrow.h"
+#include "Box.h"
+#include "Cone.h"
+#include "Contour2D.h"
+#include "Contour3D.h"
+#include "Cutplane.h"
+#include "Cylinder.h"
+#include "Disk.h"
+#include "Glyphs.h"
+#include "Group.h"
+#include "HeightMap.h"
+#include "Image.h"
+#include "ImageCutplane.h"
+#include "LIC.h"
+#include "Line.h"
+#include "Molecule.h"
+#include "Outline.h"
+#include "Parallelepiped.h"
+#include "PolyData.h"
+#include "Polygon.h"
+#include "PseudoColor.h"
+#include "Sphere.h"
+#include "Streamlines.h"
+#include "Text3D.h"
+#include "Volume.h"
+#include "Warp.h"
+#include "Trace.h"
+
+// Controls if TGA format is sent to client
+//#define RENDER_TARGA
+#define TARGA_BYTES_PER_PIXEL 3
+
+namespace VtkVis {
+
+/**
+ * \brief VTK Renderer
+ */
+class Renderer
+{
+public:
+    Renderer();
+    virtual ~Renderer();
+
+    enum AxisRangeMode {
+        RANGE_AUTO = 0,
+        RANGE_SCALE_BOUNDS,
+        RANGE_EXPLICIT
+    };
+
+    enum AxesFlyMode {
+        FLY_OUTER_EDGES = 0,
+        FLY_CLOSEST_TRIAD,
+        FLY_FURTHEST_TRIAD,
+        FLY_STATIC_EDGES,
+        FLY_STATIC_TRIAD
+    };
+
+    enum AxesTickPosition {
+        TICKS_INSIDE,
+        TICKS_OUTSIDE,
+        TICKS_BOTH
+    };
+
+    enum CameraMode {
+        PERSPECTIVE,
+        ORTHO,
+        IMAGE
+    };
+
+    enum Aspect {
+        ASPECT_NATIVE,
+        ASPECT_SQUARE,
+        ASPECT_WINDOW
+    };
+
+    enum LegendType {
+        LEGEND_SCALAR,
+        LEGEND_VECTOR_MAGNITUDE,
+        LEGEND_VECTOR_X,
+        LEGEND_VECTOR_Y,
+        LEGEND_VECTOR_Z
+    };
+
+    typedef std::string DataSetId;
+    typedef std::string ColorMapId;
+    typedef std::string FieldId;
+
+    // Data sets
+
+    void addDataSet(const DataSetId& id);
+
+    void deleteDataSet(const DataSetId& id);
+
+    DataSet *getDataSet(const DataSetId& id);
+
+    void getDataSetNames(std::vector<std::string>& names);
+
+    bool setData(const DataSetId& id, char *data, int nbytes);
+
+    bool setDataFile(const DataSetId& id, const char *filename);
+
+    bool setDataSetActiveScalars(const DataSetId& id, const char *scalarName);
+
+    bool setDataSetActiveVectors(const DataSetId& id, const char *vectorName);
+
+    bool getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value);
+
+    bool getScalarValue(const DataSetId& id, double x, double y, double z, double *value);
+
+    bool getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3]);
+
+    bool getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3]);
+
+    void setDataSetOpacity(const DataSetId& id, double opacity);
+
+    void setDataSetVisibility(const DataSetId& id, bool state);
+
+    void setUseCumulativeDataRange(bool state, bool onlyVisible = false);
+
+    bool getUseCumulativeRange();
+
+    bool setCumulativeDataRange(double *range, const char *name,
+                                DataSet::DataAttributeType type,
+                                int numComponents,
+                                int component = -1);
+
+    bool getCumulativeDataRange(double *range, const char *name,
+                                int numComponents,
+                                int component = -1);
+
+    bool getCumulativeDataRange(double *range, const char *name,
+                                DataSet::DataAttributeType type,
+                                int numComponents,
+                                int component = -1);
+
+    // Render window
+
+    /// Get the VTK render window object this Renderer uses
+    vtkRenderWindow *getRenderWindow()
+    {
+        return _renderWindow;
+    }
+
+    void setWindowSize(int width, int height);
+
+    int getWindowWidth() const;
+
+    int getWindowHeight() const;
+
+    // Lights
+
+    int addLight(float pos[3]);
+
+    vtkLight *getLight(int lightIdx);
+
+    void setLightSwitch(int lightIdx, bool state);
+
+    // Camera controls
+
+    void setViewAngle(int height);
+
+    /// Return the VTK camera object this Renderer uses
+    vtkCamera *getVtkCamera()
+    {
+        if (_renderer != NULL)
+            return _renderer->GetActiveCamera();
+        else
+            return NULL;
+    }
+
+    bool isCameraMaximized()
+    {
+        return (_cameraZoomRatio == 1.0 &&
+                _cameraPan[0] == 0.0 &&
+                _cameraPan[1] == 0.0);
+    }
+
+    void getImageCameraSizes(int *imgWidthPx, int *imgHeightPx,
+                             int *_pxOffsetX = NULL, int *_pxOffsetY = NULL);
+
+    double getImageCameraAspect();
+
+    void setCameraMode(CameraMode mode);
+
+    CameraMode getCameraMode() const;
+
+    void setCameraAspect(Aspect aspect);
+
+    void resetVtkCamera(double *bounds = NULL);
+
+    void resetCamera(bool resetOrientation = true);
+
+    void resetCameraClippingRange();
+
+    bool setCameraZoomRegionPixels(int x, int y, int width, int height);
+
+    bool setCameraZoomRegion(double x, double y, double width, double height);
+
+    void getCameraZoomRegion(double xywh[4]) const;
+
+    void getScreenWorldCoords(double xywh[4]) const;
+
+    void rotateCamera(double yaw, double pitch, double roll);
+
+    void setCameraOrientation(const double quat[4], bool absolute = true);
+
+    void panCamera(double x, double y, bool absolute = true);
+
+    void zoomCamera(double z, bool absolute = true);
+
+    void setCameraOrientationAndPosition(const double position[3],
+                                         const double focalPoint[3],
+                                         const double viewUp[3]);
+
+    void getCameraOrientationAndPosition(double position[3],
+                                         double focalPoint[3],
+                                         double viewUp[3]);
+
+    void eventuallyResetCamera()
+    {
+        _needsCameraReset = true;
+    }
+
+    bool needsCameraReset()
+    {
+        return _needsCameraReset;
+    }
+
+    void eventuallyResetCameraClippingRange()
+    {
+        _needsCameraClippingRangeReset = true;
+    }
+
+    bool needsCameraClippingRangeReset()
+    {
+        return _needsCameraClippingRangeReset;
+    }
+
+    // Rendering an image
+
+    void setBackgroundColor(float color[3]);
+
+    void setClipPlane(Axis axis, double ratio, int direction);
+
+    void setUseTwoSidedLighting(bool state);
+
+    void setUseDepthPeeling(bool state);
+
+    void setDepthPeelingParams(double occlusionRatio = 0.0, int maxPeels = 0);
+
+    void eventuallyRender();
+
+    bool render();
+
+    void getRenderedFrame(vtkUnsignedCharArray *imgData);
+
+    // Axes
+
+    void setAxesOrigin(double x, double y, double z, bool useCustom = true);
+
+    void setAxesAutoBounds(bool state);
+
+    void setAxesBounds(double min, double max);
+
+    void setAxesAutoRange(bool state);
+
+    void setAxisBounds(Axis axis, double min, double max);
+
+    void setAxesScale(double scale);
+
+    void setAxesRange(double min, double max);
+
+    void setAxesLabelPowerScaling(int xPow, int yPow, int zPow, bool useCustom = true);
+
+    void setAxisAutoBounds(Axis axis, bool state);
+
+    void setAxisAutoRange(Axis axis, bool state);
+
+    void setAxisScale(Axis axis, double scale);
+
+    void setAxisRange(Axis axis, double min, double max);
+
+    void setAxesFlyMode(AxesFlyMode mode);
+
+    void setAxesVisibility(bool state);
+
+    void setAxesGridVisibility(bool state);
+
+    void setAxesInnerGridVisibility(bool state);
+
+    void setAxesGridpolysVisibility(bool state);
+
+    void setAxesLabelVisibility(bool state);
+
+    void setAxesTickVisibility(bool state);
+
+    void setAxesMinorTickVisibility(bool state);
+
+    void setAxesTickPosition(AxesTickPosition pos);
+
+    void setAxesColor(double color[3], double opacity = 1.0);
+
+    void setAxesTitleColor(double color[3], double opacity = 1.0);
+
+    void setAxesLabelColor(double color[3], double opacity = 1.0);
+
+    void setAxesLinesColor(double color[3], double opacity = 1.0);
+
+    void setAxesGridlinesColor(double color[3], double opacity = 1.0);
+
+    void setAxesInnerGridlinesColor(double color[3], double opacity = 1.0);
+
+    void setAxesGridpolysColor(double color[3], double opacity = 1.0);
+
+    void setAxesLabelScaling(bool autoScale, int xpow, int ypow, int zpow);
+
+    void setAxesPixelFontSize(double screenSize);
+
+    void setAxesTitleFont(const char *fontName);
+
+    void setAxesTitleFontSize(int sz);
+
+    void setAxesTitleOrientation(double orientation);
+
+    void setAxesLabelFont(const char *fontName);
+
+    void setAxesLabelFontSize(int sz);
+
+    void setAxesLabelOrientation(double orientation);
+
+    void setAxesLabelFormat(const char *format);
+
+    void setAxisVisibility(Axis axis, bool state);
+
+    void setAxisGridVisibility(Axis axis, bool state);
+
+    void setAxisInnerGridVisibility(Axis axis, bool state);
+
+    void setAxisGridpolysVisibility(Axis axis, bool state);
+
+    void setAxisLabelVisibility(Axis axis, bool state);
+
+    void setAxisTickVisibility(Axis axis, bool state);
+
+    void setAxisMinorTickVisibility(Axis axis, bool state);
+
+    void setAxisColor(Axis axis, double color[3], double opacity = 1.0);
+
+    void setAxisTitleColor(Axis axis, double color[3], double opacity = 1.0);
+
+    void setAxisLabelColor(Axis axis, double color[3], double opacity = 1.0);
+
+    void setAxisLinesColor(Axis axis, double color[3], double opacity = 1.0);
+
+    void setAxisGridlinesColor(Axis axis, double color[3], double opacity = 1.0);
+
+    void setAxisInnerGridlinesColor(Axis axis, double color[3], double opacity = 1.0);
+
+    void setAxisGridpolysColor(Axis axis, double color[3], double opacity = 1.0);
+
+    void setAxisTitle(Axis axis, const char *title);
+
+    void setAxisUnits(Axis axis, const char *units);
+
+    void setAxisTitleFont(Axis axis, const char *fontName);
+
+    void setAxisTitleFontSize(Axis axis, int sz);
+
+    void setAxisTitleOrientation(Axis axis, double orientation);
+
+    void setAxisLabelFont(Axis axis, const char *fontName);
+
+    void setAxisLabelFontSize(Axis axis, int sz);
+
+    void setAxisLabelOrientation(Axis axis, double orientation);
+
+    void setAxisLabelFormat(Axis axis, const char *format);
+
+    void eventuallyResetAxes()
+    {
+        _needsAxesReset = true;
+    }
+
+    bool needsAxesReset()
+    {
+        return _needsAxesReset;
+    }
+
+    // Colormaps
+
+    void addColorMap(const ColorMapId& id, ColorMap *colorMap);
+
+    void deleteColorMap(const ColorMapId& id);
+
+    ColorMap *getColorMap(const ColorMapId& id);
+
+    void setColorMapNumberOfTableEntries(const ColorMapId& id, int numEntries);
+
+    bool renderColorMap(const ColorMapId& id,
+                        int width, int height,
+                        bool opaque,
+                        vtkUnsignedCharArray *imgData);
+
+    bool renderColorMap(const ColorMapId& id, 
+                        const DataSetId& dataSetID,
+                        LegendType legendType,
+                        const char *fieldName,
+                        DataSet::DataAttributeType type,
+                        std::string& title,
+                        double range[2],
+                        int width, int height,
+                        bool opaque,
+                        int numLabels,
+                        vtkUnsignedCharArray *imgData);
+
+    bool renderColorMap(const ColorMapId& id, 
+                        const DataSetId& dataSetID,
+                        LegendType legendType,
+                        const char *fieldName,
+                        std::string& title,
+                        double range[2],
+                        int width, int height,
+                        bool opaque,
+                        int numLabels,
+                        vtkUnsignedCharArray *imgData);
+
+    bool renderColorMap(const ColorMapId& id, 
+                        const DataSetId& dataSetID,
+                        LegendType legendType,
+                        std::string& title,
+                        double range[2],
+                        int width, int height,
+                        bool opaque,
+                        int numLabels,
+                        vtkUnsignedCharArray *imgData);
+
+    // Generic GraphicsObject methods
+
+    GraphicsObject *getGenericGraphicsObject(const DataSetId& id);
+
+    template<class T>
+    T *getGraphicsObject(const DataSetId& id);
+
+    template<class T>
+    bool addGraphicsObject(const DataSetId& id);
+
+    template<class T>
+    void deleteGraphicsObject(const DataSetId& id);
+
+    template<class T>
+    void deleteAllGraphicsObjects();
+
+    template<class T>
+    void mergeGraphicsObjectBounds(double *bounds, bool onlyVisible);
+
+    template<class T>
+    void mergeGraphicsObjectUnscaledBounds(double *bounds, bool onlyVisible);
+
+    template<class T>
+    void updateGraphicsObjectFieldRanges();
+
+    template<class T>
+    void setGraphicsObjectClippingPlanes(vtkPlaneCollection *planes);
+
+    template<class T>
+    void setGraphicsObjectAspect(double aspectRatio);
+
+    template<class T>
+    void setGraphicsObjectInterpolateBeforeMapping(const DataSetId& id, bool state);
+
+    template<class T>
+    void setGraphicsObjectColorMap(const DataSetId& id, const ColorMapId& colorMapId);
+
+    template<class T>
+    void updateGraphicsObjectColorMap(ColorMap *cmap);
+
+    template<class T>
+    bool graphicsObjectColorMapUsed(ColorMap *cmap);
+
+    template<class T>
+    void setGraphicsObjectVolumeSlice(const DataSetId& id, Axis axis, double ratio);
+
+    // Prop/Prop3D properties
+
+    template<class T>
+    void setGraphicsObjectOrientation(const DataSetId& id, double quat[4]);
+
+    template<class T>
+    void setGraphicsObjectOrientation(const DataSetId& id, double angle, double axis[3]);
+
+    template<class T>
+    void setGraphicsObjectOrigin(const DataSetId& id, double origin[3]);
+
+    template<class T>
+    void setGraphicsObjectPosition(const DataSetId& id, double pos[3]);
+
+    template<class T>
+    void setGraphicsObjectAspect(const DataSetId& id, double aspect);
+
+    template<class T>
+    void setGraphicsObjectScale(const DataSetId& id, double scale[3]);
+
+    template<class T>
+    void setGraphicsObjectTransform(const DataSetId& id, vtkMatrix4x4 *trans);
+
+    template<class T>
+    void setGraphicsObjectVisibility(const DataSetId& id, bool state);
+
+    // Actor properties
+
+    template<class T>
+    void setGraphicsObjectColor(const DataSetId& id, float color[3]);
+
+    template<class T>
+    void setGraphicsObjectCullFace(const DataSetId& id, GraphicsObject::CullFace state);
+
+    template<class T>
+    void setGraphicsObjectCulling(const DataSetId& id, bool state);
+
+    template<class T>
+    void setGraphicsObjectEdgeVisibility(const DataSetId& id, bool state);
+
+    template<class T>
+    void setGraphicsObjectEdgeColor(const DataSetId& id, float color[3]);
+
+    template<class T>
+    void setGraphicsObjectEdgeWidth(const DataSetId& id, float edgeWidth);
+
+    template<class T>
+    void setGraphicsObjectAmbient(const DataSetId& id, double coeff);
+
+    template<class T>
+    void setGraphicsObjectDiffuse(const DataSetId& id, double coeff);
+
+    template<class T>
+    void setGraphicsObjectShadingModel(const DataSetId& id, GraphicsObject::ShadingModel state);
+
+    template<class T>
+    void setGraphicsObjectSpecular(const DataSetId& id, double coeff, double power);
+
+    template<class T>
+    void setGraphicsObjectLighting(const DataSetId& id, bool state);
+
+    template<class T>
+    void setGraphicsObjectOpacity(const DataSetId& id, double opacity);
+
+    template<class T>
+    void setGraphicsObjectPointSize(const DataSetId& id, float size);
+
+    template<class T>
+    void setGraphicsObjectWireframe(const DataSetId& id, bool state);
+
+    // For Shapes:
+
+    template<class T>
+    void setGraphicsObjectFlipNormals(const DataSetId& id, bool state);
+
+    // Arcs
+
+    bool addArc(const DataSetId& id, double center[3], double pt1[3],
+                double normal[3], double angle);
+
+    void setArcResolution(const DataSetId& id, int res);
+
+    // Arrows
+
+    bool addArrow(const DataSetId& id, double tipRadius, double shaftRadius,
+                  double tipLength, bool flipNormals = false);
+
+    void setArrowResolution(const DataSetId& id, int resTip, int resShaft);
+
+    // Boxes
+
+    bool addBox(const DataSetId& id, double xLen, double yLen, double zLen,
+                bool flipNormals = false);
+
+    // Cones
+
+    bool addCone(const DataSetId& id, double radius, double height, bool cap,
+                 bool flipNormals = false);
+
+    void setConeResolution(const DataSetId& id, int res);
+
+    // 2D Contour plots
+
+    bool addContour2D(const DataSetId& id, int numContours);
+
+    bool addContour2D(const DataSetId& id, const std::vector<double>& contours);
+
+    void setContour2DContourField(const DataSetId& id, const char *fieldName);
+
+    void setContour2DNumContours(const DataSetId& id, int numContours);
+
+    void setContour2DContourList(const DataSetId& id, const std::vector<double>& contours);
+
+    void setContour2DColorMode(const DataSetId& id,
+                               Contour2D::ColorMode mode,
+                               const char *name, double range[2] = NULL);
+
+    void setContour2DColorMode(const DataSetId& id,
+                               Contour2D::ColorMode mode,
+                               DataSet::DataAttributeType type,
+                               const char *name, double range[2] = NULL);
+
+    // 3D Contour (isosurface) plots
+
+    bool addContour3D(const DataSetId& id, int numContours);
+
+    bool addContour3D(const DataSetId& id, const std::vector<double>& contours);
+
+    void setContour3DContourField(const DataSetId& id, const char *fieldName);
+
+    void setContour3DNumContours(const DataSetId& id, int numContours);
+
+    void setContour3DContourList(const DataSetId& id, const std::vector<double>& contours);
+
+    void setContour3DColorMode(const DataSetId& id,
+                               Contour3D::ColorMode mode,
+                               const char *name, double range[2] = NULL);
+
+    void setContour3DColorMode(const DataSetId& id,
+                               Contour3D::ColorMode mode,
+                               DataSet::DataAttributeType type,
+                               const char *name, double range[2] = NULL);
+
+    // Cutplanes
+
+    void setCutplaneCloudStyle(const DataSetId& id,
+                               Cutplane::CloudStyle style);
+
+    void setCutplaneOutlineVisibility(const DataSetId& id, bool state);
+
+    void setCutplaneSliceVisibility(const DataSetId& id, Axis axis, bool state);
+
+    void setCutplaneColorMode(const DataSetId& id,
+                              Cutplane::ColorMode mode,
+                              const char *name, double range[2] = NULL);
+
+    void setCutplaneColorMode(const DataSetId& id,
+                              Cutplane::ColorMode mode,
+                              DataSet::DataAttributeType type,
+                              const char *name, double range[2] = NULL);
+
+    // Cylinders
+
+    bool addCylinder(const DataSetId& id, double radius, double height, bool cap, bool flipNormals = false);
+
+    void setCylinderCapping(const DataSetId& id, bool state);
+
+    void setCylinderResolution(const DataSetId& id, int res);
+
+    // Disks
+
+    bool addDisk(const DataSetId& id, double innerRadius, double outerRadius, bool flipNormals = false);
+
+    void setDiskResolution(const DataSetId& id, int resRadial, int resCircum);
+
+    // Glyphs
+
+    bool addGlyphs(const DataSetId& id, Glyphs::GlyphShape shape);
+
+    void setGlyphsShape(const DataSetId& id, Glyphs::GlyphShape shape);
+
+    void setGlyphsOrientMode(const DataSetId& id, bool state, const char *name);
+
+    void setGlyphsQuality(const DataSetId& id, double quality);
+
+    void setGlyphsColorMode(const DataSetId& id,
+                            Glyphs::ColorMode mode,
+                            const char *name,
+                            double range[2] = NULL);
+
+    void setGlyphsScalingMode(const DataSetId& id,
+                              Glyphs::ScalingMode mode,
+                              const char *name,
+                              double range[2] = NULL);
+
+    void setGlyphsNormalizeScale(const DataSetId& id, bool normalize);
+
+    void setGlyphsScaleFactor(const DataSetId& id, double scale);
+
+    void setGlyphsMaximumNumberOfGlyphs(const DataSetId& id, int max,
+                                        bool random = true,
+                                        int offset = 0, int ratio = 1);
+
+    // Groups
+
+    bool addGroup(const DataSetId& id, const std::vector<Group::NodeId>& nodeList);
+
+    void addChildrenToGroup(const DataSetId& id, const std::vector<Group::NodeId>& nodeList);
+
+    void removeChildrenFromGroup(const DataSetId& id, const std::vector<Group::NodeId>& nodeList);
+
+    // Height maps
+
+    bool addHeightMap(const DataSetId& id, int numContours, double heightScale);
+
+    bool addHeightMap(const DataSetId& id, const std::vector<double>& contours, double heightScale);
+
+    void setHeightMapHeightScale(const DataSetId& id, double scale);
+
+    void setHeightMapCloudStyle(const DataSetId& id,
+                                HeightMap::CloudStyle style);
+
+    void setHeightMapNumContours(const DataSetId& id, int numContours);
+
+    void setHeightMapContourList(const DataSetId& id, const std::vector<double>& contours);
+
+    void setHeightMapContourSurfaceVisibility(const DataSetId& id, bool state);
+
+    void setHeightMapContourLineVisibility(const DataSetId& id, bool state);
+
+    void setHeightMapContourLineColorMapEnabled(const DataSetId& id, bool mode);
+
+    void setHeightMapContourEdgeColor(const DataSetId& id, float color[3]);
+
+    void setHeightMapContourEdgeWidth(const DataSetId& id, float edgeWidth);
+
+    void setHeightMapColorMode(const DataSetId& id,
+                               HeightMap::ColorMode mode,
+                               const char *name, double range[2] = NULL);
+
+    void setHeightMapColorMode(const DataSetId& id,
+                               HeightMap::ColorMode mode,
+                               DataSet::DataAttributeType type,
+                               const char *name, double range[2] = NULL);
+
+    // Images
+
+    void setImageBackground(const DataSetId& id, bool state);
+
+    void setImageBacking(const DataSetId& id, bool state);
+
+    void setImageBorder(const DataSetId& id, bool state);
+
+    // For ImageActor/ImageSliceMapper only
+    void setImageExtents(const DataSetId& id, int extents[6]);
+
+    void setImageLevel(const DataSetId& id, double level);
+
+    void setImageWindow(const DataSetId& id, double window);
+
+    void setImageSliceInterp(const DataSetId& id, bool state);
+
+    void setImageSlicePlane(const DataSetId& id, double normal[3], double origin[3]);
+
+    void setImageSliceFollowsCamera(const DataSetId& id, bool state);
+
+    // For ImageActor/ImageSliceMapper only
+    void setImageZSlice(const DataSetId& id, int z);
+
+    // ImageCutplanes
+
+    void setImageCutplaneOutlineVisibility(const DataSetId& id, bool state);
+
+    void setImageCutplaneSliceVisibility(const DataSetId& id, Axis axis, bool state);
+
+    void setImageCutplaneLevel(const DataSetId& id, double level);
+
+    void setImageCutplaneWindow(const DataSetId& id, double window);
+
+    // Lines
+
+    bool addLine(const DataSetId& id, double pt1[3], double pt2[3]);
+
+    bool addLine(const DataSetId& id, std::vector<double> points);
+
+    // Molecules
+
+    void setMoleculeAtomQuality(const DataSetId& id, double quality);
+
+    void setMoleculeBondQuality(const DataSetId& id, double quality);
+
+    void setMoleculeAtomRadiusScale(const DataSetId& id, double scale);
+
+    void setMoleculeBondRadiusScale(const DataSetId& id, double scale);
+
+    void setMoleculeAtomScaling(const DataSetId& id, Molecule::AtomScaling scaling);
+
+    void setMoleculeAtomVisibility(const DataSetId& id, bool state);
+
+    void setMoleculeAtomLabelField(const DataSetId& id, const char *fieldName);
+
+    void setMoleculeAtomLabelVisibility(const DataSetId& id, bool state);
+
+    void setMoleculeBondVisibility(const DataSetId& id, bool state);
+
+    void setMoleculeBondStyle(const DataSetId& id, Molecule::BondStyle style);
+
+    void setMoleculeBondColorMode(const DataSetId& id, Molecule::BondColorMode mode);
+
+    void setMoleculeBondColor(const DataSetId& id, float color[3]);
+
+    void setMoleculeColorMode(const DataSetId& id,
+                              Molecule::ColorMode mode,
+                              const char *name, double range[2] = NULL);
+
+    void setMoleculeColorMode(const DataSetId& id,
+                              Molecule::ColorMode mode,
+                              DataSet::DataAttributeType type,
+                              const char *name, double range[2] = NULL);
+
+    // Parallelepipeds
+
+    bool addParallelepiped(const DataSetId& id, double vec1[3], double vec2[3], double vec3[3],
+                           bool flipNormals = false);
+
+    // PolyData meshes
+
+    void setPolyDataCloudStyle(const DataSetId& id,
+                               PolyData::CloudStyle style);
+
+    void setPolyDataColorMode(const DataSetId& id,
+                              PolyData::ColorMode mode,
+                              const char *name, double range[2] = NULL);
+
+    void setPolyDataColorMode(const DataSetId& id,
+                              PolyData::ColorMode mode,
+                              DataSet::DataAttributeType type,
+                              const char *name, double range[2] = NULL);
+
+    // N-sided Regular Polygons
+
+    bool addPolygon(const DataSetId& id, int numSides, double center[3], double normal[3], double radius);
+
+    // Color-mapped surfaces
+
+    void setPseudoColorCloudStyle(const DataSetId& id,
+                                  PseudoColor::CloudStyle style);
+
+    void setPseudoColorColorMode(const DataSetId& id,
+                                 PseudoColor::ColorMode mode,
+                                 const char *name, double range[2] = NULL);
+
+    void setPseudoColorColorMode(const DataSetId& id,
+                                 PseudoColor::ColorMode mode,
+                                 DataSet::DataAttributeType type,
+                                 const char *name, double range[2] = NULL);
+
+    // Spheres
+
+    bool addSphere(const DataSetId& id, double center[3], double radius, bool flipNormals = false);
+
+    void setSphereSection(const DataSetId& id,
+                          double thetaStart, double thetaEnd,
+                          double phiStart, double phiEnd);
+
+    void setSphereResolution(const DataSetId& id, int thetaRes, int phiRes);
+
+    // Streamlines
+
+    void setStreamlinesColorMode(const DataSetId& id,
+                                 Streamlines::ColorMode mode,
+                                 const char *name, double range[2] = NULL);
+
+    void setStreamlinesColorMode(const DataSetId& id,
+                                 Streamlines::ColorMode mode,
+                                 DataSet::DataAttributeType type,
+                                 const char *name, double range[2] = NULL);
+
+    void setStreamlinesLength(const DataSetId& id, double length);
+
+    void setStreamlinesNumberOfSeedPoints(const DataSetId& id, int numPoints);
+
+    void setStreamlinesSeedColor(const DataSetId& id, float color[3]);
+
+    void setStreamlinesSeedPointSize(const DataSetId& id, float size);
+
+    void setStreamlinesSeedToMeshPoints(const DataSetId& id,
+                                        int maxPoints = 500);
+
+    void setStreamlinesSeedToFilledMesh(const DataSetId& id, int numPoints);
+
+    bool setStreamlinesSeedToMeshPoints(const DataSetId& id,
+                                        char *data, size_t nbytes,
+                                        int maxPoints = 500);
+
+    bool setStreamlinesSeedToFilledMesh(const DataSetId& id,
+                                        char *data, size_t nbytes,
+                                        int numPoints);
+
+    void setStreamlinesSeedToRake(const DataSetId& id,
+                                  double start[3], double end[3],
+                                  int numPoints);
+
+    void setStreamlinesSeedToDisk(const DataSetId& id,
+                                  double center[3], double normal[3],
+                                  double radius, double innerRadius,
+                                  int numPoints);
+
+    void setStreamlinesSeedToPolygon(const DataSetId& id,
+                                     double center[3], double normal[3],
+                                     double angle, double radius,
+                                     int numSides);
+
+    void setStreamlinesSeedToFilledPolygon(const DataSetId& id,
+                                           double center[3], double normal[3],
+                                           double angle, double radius,
+                                           int numSides, int numPoints);
+
+    void setStreamlinesSeedVisibility(const DataSetId& id, bool state);
+
+    void setStreamlinesTerminalSpeed(const DataSetId& id, double speed);
+
+    void setStreamlinesTypeToLines(const DataSetId& id);
+
+    void setStreamlinesTypeToTubes(const DataSetId& id, int numSides, double radius);
+
+    void setStreamlinesTypeToRibbons(const DataSetId& id, double width, double angle);
+
+    // Text3Ds
+
+    bool addText3D(const DataSetId& id, const char *string,
+                   const char *fontFamily, int fontSize,
+                   bool bold = false, bool italic = false, bool shadow = false);
+
+    void setText3DBold(const DataSetId& id, bool state);
+
+    void setText3DFollowCamera(const DataSetId& id, bool state);
+
+    void setText3DFont(const DataSetId& id, const char *fontFamily);
+
+    void setText3DFontSize(const DataSetId& id, int size);
+
+    void setText3DItalic(const DataSetId& id, bool state);
+
+    void setText3DShadow(const DataSetId& id, bool state);
+
+    void setText3DText(const DataSetId& id, const char *text);
+
+    // Volumes
+
+    void setVolumeBlendMode(const DataSetId& id, Volume::BlendMode mode);
+
+    void setVolumeSampleDistance(const DataSetId& id, double distance);
+
+    // Warps
+
+    void setWarpCloudStyle(const DataSetId& id,
+                           Warp::CloudStyle style);
+
+    void setWarpColorMode(const DataSetId& id,
+                          Warp::ColorMode mode,
+                          const char *name, double range[2] = NULL);
+
+    void setWarpColorMode(const DataSetId& id,
+                          Warp::ColorMode mode,
+                          DataSet::DataAttributeType type,
+                          const char *name, double range[2] = NULL);
+
+
+    void setWarpWarpScale(const DataSetId& id, double scale);
+
+private:
+    typedef std::tr1::unordered_map<DataSetId, DataSet *> DataSetHashmap;
+    typedef std::tr1::unordered_map<FieldId, double *> FieldRangeHashmap;
+    typedef std::tr1::unordered_map<ColorMapId, ColorMap *> ColorMapHashmap;
+
+    typedef std::tr1::unordered_map<DataSetId, Arc *> ArcHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Arrow *> ArrowHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Box *> BoxHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Cone *> ConeHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Contour2D *> Contour2DHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Contour3D *> Contour3DHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Cutplane *> CutplaneHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Cylinder *> CylinderHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Disk *> DiskHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Glyphs *> GlyphsHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Group *> GroupHashmap;
+    typedef std::tr1::unordered_map<DataSetId, HeightMap *> HeightMapHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Image *> ImageHashmap;
+    typedef std::tr1::unordered_map<DataSetId, ImageCutplane *> ImageCutplaneHashmap;
+    typedef std::tr1::unordered_map<DataSetId, LIC *> LICHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Line *> LineHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Molecule *> MoleculeHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Outline *> OutlineHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Parallelepiped *> ParallelepipedHashmap;
+    typedef std::tr1::unordered_map<DataSetId, PolyData *> PolyDataHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Polygon *> PolygonHashmap;
+    typedef std::tr1::unordered_map<DataSetId, PseudoColor *> PseudoColorHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Sphere *> SphereHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Streamlines *> StreamlinesHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Text3D *> Text3DHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Volume *> VolumeHashmap;
+    typedef std::tr1::unordered_map<DataSetId, Warp *> WarpHashmap;
+
+    void _setCameraZoomRegionPixels(int x, int y, int width, int height);
+
+    void _setCameraZoomRegion(double x, double y, double width, double height);
+
+    //static void printCameraInfo(vtkCamera *camera);
+
+    static void setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4 &mat);
+
+    static void mergeBounds(double *boundsDest, const double *bounds1, const double *bounds2);
+
+    template<class T>
+    std::tr1::unordered_map<DataSetId, T *>& getGraphicsObjectHashmap();
+
+    void setObjectAspects(double aspectRatio);
+
+    void collectBounds(double *bounds, bool onlyVisible);
+
+    void collectUnscaledBounds(double *bounds, bool onlyVisible);
+
+    void collectDataRanges();
+
+    void collectDataRanges(double *range, const char *name,
+                           DataSet::DataAttributeType type,
+                           int component, bool onlyVisible);
+
+    void clearFieldRanges();
+
+    void clearUserFieldRanges();
+
+    void initFieldRanges();
+
+    void updateFieldRanges();
+
+    void updateColorMap(ColorMap *cmap);
+
+    bool colorMapUsed(ColorMap *cmap);
+
+    void computeDisplayToWorld(double x, double y, double z, double worldPt[4]);
+
+    void computeWorldToDisplay(double x, double y, double z, double displayPt[3]);
+
+    void computeScreenWorldCoords();
+
+    bool is2D(const double bounds[6],
+              PrincipalPlane *plane,
+              double *offset) const;
+
+    void initCamera(bool initCameraMode = false);
+
+    void sceneBoundsChanged();
+
+    void initOrientationMarkers();
+
+    void initAxes();
+
+    void resetAxes(double bounds[6] = NULL);
+
+    void setAxesBounds(double bounds[6] = NULL);
+
+    void setAxesRanges();
+
+    void computeAxesScale();
+
+    void setCameraClippingPlanes();
+
+    bool _needsRedraw;
+    bool _needsAxesReset;
+    bool _needsCameraClippingRangeReset;
+    bool _needsCameraReset;
+
+    int _windowWidth, _windowHeight;
+    CameraMode _cameraMode;
+    Aspect _cameraAspect;
+    bool _computeOnlyVisibleBounds;
+    double _imgWorldOrigin[2];
+    double _imgWorldDims[2];
+    double _imgWindowWorldDims[2];
+    double _userImgWorldOrigin[2];
+    double _userImgWorldDims[2];
+    PrincipalPlane _imgCameraPlane;
+    double _imgCameraOffset;
+    double _screenWorldCoords[4];
+    double _cameraOrientation[4];
+    double _cameraZoomRatio;
+    double _cameraPan[2];
+    float _bgColor[3];
+    bool _useCumulativeRange;
+    bool _cumulativeRangeOnlyVisible;
+
+    FieldRangeHashmap _scalarPointDataRange;
+    FieldRangeHashmap _vectorPointDataRange;
+    FieldRangeHashmap _vectorCompPointDataRange[3];
+    FieldRangeHashmap _scalarCellDataRange;
+    FieldRangeHashmap _vectorCellDataRange;
+    FieldRangeHashmap _vectorCompCellDataRange[3];
+    FieldRangeHashmap _scalarFieldDataRange;
+    FieldRangeHashmap _vectorFieldDataRange;
+    FieldRangeHashmap _vectorCompFieldDataRange[3];
+
+    FieldRangeHashmap _userScalarPointDataRange;
+    FieldRangeHashmap _userVectorPointDataRange;
+    FieldRangeHashmap _userVectorCompPointDataRange[3];
+    FieldRangeHashmap _userScalarCellDataRange;
+    FieldRangeHashmap _userVectorCellDataRange;
+    FieldRangeHashmap _userVectorCompCellDataRange[3];
+    FieldRangeHashmap _userScalarFieldDataRange;
+    FieldRangeHashmap _userVectorFieldDataRange;
+    FieldRangeHashmap _userVectorCompFieldDataRange[3];
+
+    bool _axesAutoBounds[3];
+    double _axesUserBounds[6];
+    AxisRangeMode _axesRangeMode[3];
+    double _axesScale[3];
+
+    ColorMapHashmap _colorMaps;
+    DataSetHashmap _dataSets;
+    ArcHashmap _arcs;
+    ArrowHashmap _arrows;
+    BoxHashmap _boxes;
+    ConeHashmap _cones;
+    Contour2DHashmap _contour2Ds;
+    Contour3DHashmap _contour3Ds;
+    CutplaneHashmap _cutplanes;
+    CylinderHashmap _cylinders;
+    DiskHashmap _disks;
+    GlyphsHashmap _glyphs;
+    GroupHashmap _groups;
+    HeightMapHashmap _heightMaps;
+    ImageHashmap _images;
+    ImageCutplaneHashmap _imageCutplanes;
+    LICHashmap _lics;
+    LineHashmap _lines;
+    MoleculeHashmap _molecules;
+    OutlineHashmap _outlines;
+    ParallelepipedHashmap _parallelepipeds;
+    PolyDataHashmap _polyDatas;
+    PolygonHashmap _polygons;
+    PseudoColorHashmap _pseudoColors;
+    SphereHashmap _spheres;
+    StreamlinesHashmap _streamlines;
+    Text3DHashmap _text3Ds;
+    VolumeHashmap _volumes;
+    WarpHashmap _warps;
+
+    vtkSmartPointer<vtkPlane> _cameraClipPlanes[4];
+    vtkSmartPointer<vtkPlane> _userClipPlanes[6];
+    vtkSmartPointer<vtkPlaneCollection> _activeClipPlanes;
+#ifdef USE_CUSTOM_AXES
+    vtkSmartPointer<vtkRpCubeAxesActor> _cubeAxesActor;
+#else
+    vtkSmartPointer<vtkCubeAxesActor> _cubeAxesActor;
+#endif
+    vtkSmartPointer<vtkAxesActor> _axesActor;
+    vtkSmartPointer<vtkAnnotatedCubeActor> _annotatedCubeActor;
+    vtkSmartPointer<vtkScalarBarActor> _scalarBarActor;
+    vtkSmartPointer<vtkRenderer> _renderer;
+    vtkSmartPointer<vtkRenderer> _legendRenderer;
+    vtkSmartPointer<vtkRenderWindow> _renderWindow;
+    vtkSmartPointer<vtkRenderWindow> _legendRenderWindow;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/RendererCmd.cpp
===================================================================
--- vtkvis/tags/1.8.4/RendererCmd.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/RendererCmd.cpp	(revision 6184)
@@ -0,0 +1,13274 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <cfloat>
+#include <cerrno>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/uio.h>
+#include <tcl.h>
+
+#include "Trace.h"
+#include "CmdProc.h"
+#include "ReadBuffer.h"
+#include "Types.h"
+#include "RendererCmd.h"
+#include "RenderServer.h"
+#include "Renderer.h"
+#include "RendererGraphicsObjs.h"
+#include "PPMWriter.h"
+#include "TGAWriter.h"
+#ifdef USE_THREADS
+#include "ResponseQueue.h"
+#endif
+
+using namespace VtkVis;
+
+static int lastCmdStatus;
+
+#ifdef USE_THREADS
+void
+VtkVis::queueResponse(ClientData clientData,
+                      const void *bytes, size_t len, 
+                      Response::AllocationType allocType,
+                      Response::ResponseType type)
+{
+    ResponseQueue *queue = (ResponseQueue *)clientData;
+
+    Response *response = new Response(type);
+    response->setMessage((unsigned char *)bytes, len, allocType);
+    queue->enqueue(response);
+}
+#else
+
+static ssize_t
+SocketWrite(const void *bytes, size_t len)
+{
+    size_t ofs = 0;
+    ssize_t bytesWritten;
+    while ((bytesWritten = write(g_fdOut, (const char *)bytes + ofs, len - ofs)) > 0) {
+        ofs += bytesWritten;
+        if (ofs == len)
+            break;
+    }
+    if (bytesWritten < 0) {
+        ERROR("write: %s", strerror(errno));
+    }
+    return bytesWritten;
+}
+
+#endif  /*USE_THREADS*/
+
+static bool
+SocketRead(char *bytes, size_t len)
+{
+    ReadBuffer::BufferStatus status;
+    status = g_inBufPtr->followingData((unsigned char *)bytes, len);
+    TRACE("followingData status: %d", status);
+    return (status == ReadBuffer::OK);
+}
+
+static int
+ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr) 
+{
+    int result;
+#ifdef WANT_TRACE
+    char *str = Tcl_DStringValue(dsPtr);
+    std::string cmd(str);
+    cmd.erase(cmd.find_last_not_of(" \n\r\t")+1);
+    TRACE("command %lu: '%s'", g_stats.nCommands+1, cmd.c_str());
+#endif
+    lastCmdStatus = TCL_OK;
+    result = Tcl_EvalEx(interp, Tcl_DStringValue(dsPtr), 
+                        Tcl_DStringLength(dsPtr), 
+                        TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
+    Tcl_DStringSetLength(dsPtr, 0);
+    if (lastCmdStatus == TCL_BREAK) {
+        return TCL_BREAK;
+    }
+    lastCmdStatus = result;
+    if (result != TCL_OK) {
+        TRACE("Error: %d", result);
+    }
+    return result;
+}
+
+static int
+GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
+{
+    int value;
+
+    if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    *boolPtr = (bool)value;
+    return TCL_OK;
+}
+
+static int
+GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
+{
+    double value;
+
+    if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    *valuePtr = (float)value;
+    return TCL_OK;
+}
+
+static int
+ArcAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+         Tcl_Obj *const *objv)
+{
+    double center[3], pt1[3], norm[3], angle;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &center[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &center[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &center[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &pt1[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[6], &pt1[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[7], &pt1[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[8], &norm[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[9], &norm[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[10], &norm[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[11], &angle) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[12]);
+    if (!g_renderer->addArc(name, center, pt1, norm, angle)) {
+        Tcl_AppendResult(interp, "Failed to create arc", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+ArcDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Arc>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Arc>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+ArcColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Arc>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Arc>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Arc>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Arc>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Arc>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Arc>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Arc>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Arc>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Arc>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Arc>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Arc>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Arc>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcResolutionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    int res;
+    if (Tcl_GetIntFromObj(interp, objv[2], &res) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setArcResolution(name, res);
+    } else {
+        g_renderer->setArcResolution("all", res);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Arc>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Arc>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+ArcVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Arc>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Arc>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec arcOps[] = {
+    {"add",       1, ArcAddOp, 13, 13, "centerX centerY centerZ startX startY startZ normX normY normZ angle name"},
+    {"color",     1, ArcColorOp, 5, 6, "r g b ?name?"},
+    {"delete",    1, ArcDeleteOp, 2, 3, "?name?"},
+    {"linecolor", 5, ArcColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, ArcLineWidthOp, 3, 4, "width ?name?"},
+    {"opacity",   2, ArcOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, ArcOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, ArcOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       2, ArcPositionOp, 5, 6, "x y z ?name?"},
+    {"resolution",1, ArcResolutionOp, 3, 4, "res ?name?"},
+    {"scale",     2, ArcScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"visible",   1, ArcVisibleOp, 3, 4, "bool ?name?"}
+};
+static int nArcOps = NumCmdSpecs(arcOps);
+
+static int
+ArcCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nArcOps, arcOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ArrowAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    double tipRadius, shaftRadius, tipLength;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &tipRadius) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &shaftRadius) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &tipLength) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[5]);
+    if (!g_renderer->addArrow(name, tipRadius, shaftRadius, tipLength)) {
+        Tcl_AppendResult(interp, "Failed to create arrow", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Arrow>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Arrow>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Arrow>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Arrow>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Arrow>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Arrow>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Arrow>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Arrow>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Arrow>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Arrow>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Arrow>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Arrow>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Arrow>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Arrow>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Arrow>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Arrow>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Arrow>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Arrow>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Arrow>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Arrow>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Arrow>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Arrow>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Arrow>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Arrow>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Arrow>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Arrow>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Arrow>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Arrow>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Arrow>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Arrow>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowResolutionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    int tipRes, shaftRes;
+    if (Tcl_GetIntFromObj(interp, objv[2], &tipRes) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &shaftRes) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setArrowResolution(name, tipRes, shaftRes);
+    } else {
+        g_renderer->setArrowResolution("all", tipRes, shaftRes);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Arrow>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Arrow>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Arrow>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Arrow>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Arrow>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Arrow>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ArrowWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Arrow>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Arrow>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec arrowOps[] = {
+    {"add",       1, ArrowAddOp, 6, 6, "tipRadius shaftRadius tipLength name"},
+    {"color",     2, ArrowColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, ArrowCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, ArrowDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, ArrowEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, ArrowFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, ArrowLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, ArrowLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, ArrowLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, ArrowMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, ArrowOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, ArrowOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, ArrowOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, ArrowPositionOp, 5, 6, "x y z ?name?"},
+    {"resolution",1, ArrowResolutionOp, 4, 5, "tipRes shaftRes ?name?"},
+    {"scale",     2, ArrowScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shading",   2, ArrowShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, ArrowVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, ArrowWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nArrowOps = NumCmdSpecs(arrowOps);
+
+static int
+ArrowCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nArrowOps, arrowOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+AxisAutoBoundsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool enableAuto;
+    if (GetBooleanFromObj(interp, objv[3], &enableAuto) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisAutoBounds(X_AXIS, enableAuto);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisAutoBounds(Y_AXIS, enableAuto);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisAutoBounds(Z_AXIS, enableAuto);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesAutoBounds(enableAuto);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisAutoRangeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool enableAuto;
+    if (GetBooleanFromObj(interp, objv[3], &enableAuto) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisAutoRange(X_AXIS, enableAuto);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisAutoRange(Y_AXIS, enableAuto);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisAutoRange(Z_AXIS, enableAuto);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesAutoRange(enableAuto);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double color[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double opacity = 1.0;
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &opacity) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisColor(X_AXIS, color, opacity);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisColor(Y_AXIS, color, opacity);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisColor(Z_AXIS, color, opacity);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesColor(color, opacity);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisBoundsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double min, max;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &min) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &max) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisBounds(X_AXIS, min, max);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisBounds(Y_AXIS, min, max);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisBounds(Z_AXIS, min, max);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesBounds(min, max);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisExponentOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    int xPow, yPow, zPow;
+    bool useCustom = true;
+    if (Tcl_GetIntFromObj(interp, objv[2], &xPow) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &yPow) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[4], &zPow) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc > 5) {
+        if (GetBooleanFromObj(interp, objv[5], &useCustom) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        g_renderer->setAxesLabelPowerScaling(xPow, yPow, zPow, useCustom);
+    } else {
+        g_renderer->setAxesLabelPowerScaling(xPow, yPow, zPow);
+    }
+    return TCL_OK;
+}
+
+static int
+AxisFlyModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Renderer::AxesFlyMode mode;
+    if ((c == 's') && (strcmp(string, "static_edges") == 0)) {
+        mode = Renderer::FLY_STATIC_EDGES;
+    } else if ((c == 's') && (strcmp(string, "static_triad") == 0)) {
+        mode = Renderer::FLY_STATIC_TRIAD;
+    } else if ((c == 'o') && (strcmp(string, "outer_edges") == 0)) {
+        mode = Renderer::FLY_OUTER_EDGES;
+    } else if ((c == 'f') && (strcmp(string, "furthest_triad") == 0)) {
+        mode = Renderer::FLY_FURTHEST_TRIAD;
+    } else if ((c == 'c') && (strcmp(string, "closest_triad") == 0)) {
+        mode = Renderer::FLY_CLOSEST_TRIAD;
+    } else {
+        Tcl_AppendResult(interp, "bad axis flymode option \"", string,
+                         "\": should be static_edges, static_triad, outer_edges, furthest_triad, or closest_triad", (char*)NULL);
+        return TCL_ERROR;
+    }
+    g_renderer->setAxesFlyMode(mode);
+    return TCL_OK;
+}
+
+static int
+AxisFontSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double screenSize;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &screenSize) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->setAxesPixelFontSize(screenSize);
+    return TCL_OK;
+}
+
+static int
+AxisGridOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    bool visible;
+    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisGridVisibility(X_AXIS, visible);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisGridVisibility(Y_AXIS, visible);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisGridVisibility(Z_AXIS, visible);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesGridVisibility(visible);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisGridColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double color[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double opacity = 1.0;
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &opacity) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisGridlinesColor(X_AXIS, color, opacity);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisGridlinesColor(Y_AXIS, color, opacity);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisGridlinesColor(Z_AXIS, color, opacity);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesGridlinesColor(color, opacity);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisGridpolysOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool visible;
+    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisGridpolysVisibility(X_AXIS, visible);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisGridpolysVisibility(Y_AXIS, visible);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisGridpolysVisibility(Z_AXIS, visible);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesGridpolysVisibility(visible);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisGridpolysColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    double color[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double opacity = 1.0;
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &opacity) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisGridpolysColor(X_AXIS, color, opacity);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisGridpolysColor(Y_AXIS, color, opacity);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisGridpolysColor(Z_AXIS, color, opacity);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesGridpolysColor(color, opacity);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisInnerGridOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool visible;
+    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisInnerGridVisibility(X_AXIS, visible);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisInnerGridVisibility(Y_AXIS, visible);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisInnerGridVisibility(Z_AXIS, visible);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesInnerGridVisibility(visible);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisInnerGridColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    double color[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double opacity = 1.0;
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &opacity) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisInnerGridlinesColor(X_AXIS, color, opacity);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisInnerGridlinesColor(Y_AXIS, color, opacity);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisInnerGridlinesColor(Z_AXIS, color, opacity);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesInnerGridlinesColor(color, opacity);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisLabelColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double color[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double opacity = 1.0;
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &opacity) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisLabelColor(X_AXIS, color, opacity);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisLabelColor(Y_AXIS, color, opacity);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisLabelColor(Z_AXIS, color, opacity);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesLabelColor(color, opacity);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisLabelFontOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    const char *fontName = Tcl_GetString(objv[3]);
+
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisLabelFont(X_AXIS, fontName);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisLabelFont(Y_AXIS, fontName);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisLabelFont(Z_AXIS, fontName);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesLabelFont(fontName);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisLabelFormatOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    const char *format = Tcl_GetString(objv[3]);
+
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisLabelFormat(X_AXIS, format);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisLabelFormat(Y_AXIS, format);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisLabelFormat(Z_AXIS, format);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesLabelFormat(format);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisLabelFontSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    int fontSize;
+    if (Tcl_GetIntFromObj(interp, objv[3], &fontSize) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisLabelFontSize(X_AXIS, fontSize);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisLabelFontSize(Y_AXIS, fontSize);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisLabelFontSize(Z_AXIS, fontSize);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesLabelFontSize(fontSize);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisLabelOrientationOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double rot;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &rot) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisLabelOrientation(X_AXIS, rot);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisLabelOrientation(Y_AXIS, rot);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisLabelOrientation(Z_AXIS, rot);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesLabelOrientation(rot);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisLabelScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool autoScale;
+    int xpow, ypow, zpow;
+    if (GetBooleanFromObj(interp, objv[3], &autoScale) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[4], &xpow) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[5], &ypow) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[6], &zpow) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->setAxesLabelScaling(autoScale, xpow, ypow, zpow);
+    return TCL_OK;
+}
+
+static int
+AxisLabelsVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool visible;
+    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisLabelVisibility(X_AXIS, visible);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisLabelVisibility(Y_AXIS, visible);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisLabelVisibility(Z_AXIS, visible);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesLabelVisibility(visible);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double color[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double opacity = 1.0;
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &opacity) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisLinesColor(X_AXIS, color, opacity);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisLinesColor(Y_AXIS, color, opacity);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisLinesColor(Z_AXIS, color, opacity);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesLinesColor(color, opacity);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    const char *title = Tcl_GetString(objv[3]);
+    if (strlen(title) > 60) {
+        Tcl_AppendResult(interp, "axis name \"", title,
+                         "\" is too long", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisTitle(X_AXIS, title);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisTitle(Y_AXIS, title);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisTitle(Z_AXIS, title);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double x, y, z;
+    bool useCustom = true;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &z) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc > 5) {
+        if (GetBooleanFromObj(interp, objv[5], &useCustom) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        g_renderer->setAxesOrigin(x, y, z, useCustom);
+    } else {
+        g_renderer->setAxesOrigin(x, y, z);
+    }
+    return TCL_OK;
+}
+
+static int
+AxisRangeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double min, max;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &min) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &max) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisRange(X_AXIS, min, max);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisRange(Y_AXIS, min, max);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisRange(Z_AXIS, min, max);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesRange(min, max);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double scale;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &scale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisScale(X_AXIS, scale);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisScale(Y_AXIS, scale);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisScale(Z_AXIS, scale);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesScale(scale);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisTicksVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool visible;
+    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisTickVisibility(X_AXIS, visible);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisTickVisibility(Y_AXIS, visible);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisTickVisibility(Z_AXIS, visible);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesTickVisibility(visible);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisMinorTicksVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    bool visible;
+    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisMinorTickVisibility(X_AXIS, visible);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisMinorTickVisibility(Y_AXIS, visible);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisMinorTickVisibility(Z_AXIS, visible);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesMinorTickVisibility(visible);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisTickPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'i') && (strcmp(string, "inside") == 0)) {
+        g_renderer->setAxesTickPosition(Renderer::TICKS_INSIDE);
+    } else if ((c == 'o') && (strcmp(string, "outside") == 0)) {
+        g_renderer->setAxesTickPosition(Renderer::TICKS_OUTSIDE);
+    } else if ((c == 'b') && (strcmp(string, "both") == 0)) {
+        g_renderer->setAxesTickPosition(Renderer::TICKS_BOTH);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be inside, outside or both", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisTitleColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double color[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double opacity = 1.0;
+    if (objc > 6) {
+        if (Tcl_GetDoubleFromObj(interp, objv[6], &opacity) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisTitleColor(X_AXIS, color, opacity);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisTitleColor(Y_AXIS, color, opacity);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisTitleColor(Z_AXIS, color, opacity);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesTitleColor(color, opacity);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisTitleFontOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    const char *fontName = Tcl_GetString(objv[3]);
+
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisTitleFont(X_AXIS, fontName);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisTitleFont(Y_AXIS, fontName);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisTitleFont(Z_AXIS, fontName);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesTitleFont(fontName);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisTitleFontSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    int fontSize;
+    if (Tcl_GetIntFromObj(interp, objv[3], &fontSize) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisTitleFontSize(X_AXIS, fontSize);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisTitleFontSize(Y_AXIS, fontSize);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisTitleFontSize(Z_AXIS, fontSize);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesTitleFontSize(fontSize);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisTitleOrientationOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double rot;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &rot) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisTitleOrientation(X_AXIS, rot);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisTitleOrientation(Y_AXIS, rot);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisTitleOrientation(Z_AXIS, rot);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesTitleOrientation(rot);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisUnitsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    const char *units = Tcl_GetString(objv[3]);
+    if (strlen(units) > 20) {
+        Tcl_AppendResult(interp, "axis units name \"", units,
+                         "\" is too long", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisUnits(X_AXIS, units);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisUnits(Y_AXIS, units);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisUnits(Z_AXIS, units);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxisUnits(X_AXIS, units);
+        g_renderer->setAxisUnits(Y_AXIS, units);
+        g_renderer->setAxisUnits(Z_AXIS, units);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+AxisVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool visible;
+    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        g_renderer->setAxisVisibility(X_AXIS, visible);
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        g_renderer->setAxisVisibility(Y_AXIS, visible);
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        g_renderer->setAxisVisibility(Z_AXIS, visible);
+    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
+        g_renderer->setAxesVisibility(visible);
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static CmdSpec axisOps[] = {
+    {"autobounds", 5, AxisAutoBoundsOp, 4, 4, "axis bool"},
+    {"autorange",  5, AxisAutoRangeOp, 4, 4, "axis bool"},
+    {"bounds",     1, AxisBoundsOp, 5, 5, "axis min max"},
+    {"color",      1, AxisColorOp, 6, 7, "axis r g b ?opacity?"},
+    {"exp",        1, AxisExponentOp, 5, 6, "xPow yPow zPow ?useCustom?"},
+    {"flymode",    2, AxisFlyModeOp, 3, 3, "mode"},
+    {"fontsz",     2, AxisFontSizeOp, 3, 3, "fontPixelSize"},
+    {"gpcolor",    3, AxisGridpolysColorOp, 6, 7, "axis r g b ?opacity?"},
+    {"gpolys",     3, AxisGridpolysOp, 4, 4, "axis bool"},
+    {"grcolor",    3, AxisGridColorOp, 6, 7, "axis r g b ?opacity?"},
+    {"grid",       3, AxisGridOp, 4, 4, "axis bool"},
+    {"igcolor",    3, AxisInnerGridColorOp, 6, 7, "axis r g b ?opacity?"},
+    {"igrid",      3, AxisInnerGridOp, 4, 4, "axis bool"},
+    {"labels",     2, AxisLabelsVisibleOp, 4, 4, "axis bool"},
+    {"lcolor",     2, AxisLabelColorOp, 6, 7, "axis r g b ?opacity?"},
+    {"lfont",      4, AxisLabelFontOp, 4, 4, "axis font"},
+    {"lformat",    4, AxisLabelFormatOp, 4, 4, "axis format"},
+    {"lfsize",     3, AxisLabelFontSizeOp, 4, 4, "axis fontSize"},
+    {"linecolor",  2, AxisLineColorOp, 6, 7, "axis r g b ?opacity?"},
+    {"lrot",       2, AxisLabelOrientationOp, 4, 4, "axis rot"},
+    {"lscale",     2, AxisLabelScaleOp, 7, 7, "axis bool xpow ypow zpow"},
+    {"minticks",   1, AxisMinorTicksVisibleOp, 4, 4, "axis bool"},
+    {"name",       1, AxisNameOp, 4, 4, "axis title"},
+    {"origin",     1, AxisOriginOp, 5, 6, "x y z ?useCustom?"},
+    {"range",      1, AxisRangeOp, 5, 5, "axis min max"},
+    {"scale",      1, AxisScaleOp, 4, 4, "axis scale"},
+    {"tcolor",     2, AxisTitleColorOp, 6, 7, "axis r g b ?opacity?"},
+    {"tfont",      3, AxisTitleFontOp, 4, 4, "axis font"},
+    {"tfsize",     3, AxisTitleFontSizeOp, 4, 4, "axis fontSize"},
+    {"tickpos",    5, AxisTickPositionOp, 3, 3, "position"},
+    {"ticks",      5, AxisTicksVisibleOp, 4, 4, "axis bool"},
+    {"trot",       2, AxisTitleOrientationOp, 4, 4, "axis rot"},
+    {"units",      1, AxisUnitsOp, 4, 4, "axis units"},
+    {"visible",    1, AxisVisibleOp, 4, 4, "axis bool"}
+};
+static int nAxisOps = NumCmdSpecs(axisOps);
+
+static int
+AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+        Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nAxisOps, axisOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+BoxAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+         Tcl_Obj *const *objv)
+{
+    double xLen, yLen, zLen;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &xLen) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &yLen) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &zLen) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[5]);
+    if (!g_renderer->addBox(name, xLen, yLen, zLen)) {
+        Tcl_AppendResult(interp, "Failed to create box", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+BoxDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Box>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Box>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+BoxColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Box>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Box>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Box>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Box>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Box>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Box>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Box>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Box>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Box>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Box>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Box>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Box>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Box>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Box>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Box>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Box>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Box>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Box>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Box>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Box>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Box>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Box>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Box>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Box>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Box>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Box>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Box>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Box>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Box>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Box>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Box>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Box>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Box>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Box>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+BoxWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Box>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Box>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec boxOps[] = {
+    {"add",       1, BoxAddOp, 6, 6, "xLen yLen zLen name"},
+    {"color",     2, BoxColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, BoxCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, BoxDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, BoxEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, BoxFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, BoxLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, BoxLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, BoxLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, BoxMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, BoxOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, BoxOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, BoxOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, BoxPositionOp, 5, 6, "x y z ?name?"},
+    {"scale",     2, BoxScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shading",   2, BoxShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, BoxVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, BoxWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nBoxOps = NumCmdSpecs(boxOps);
+
+static int
+BoxCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nBoxOps, boxOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+CameraAspectOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    Renderer::Aspect aspect;
+    const char *string = Tcl_GetString(objv[2]);
+    if (string[0] == 'n' && (strcmp(string, "native") == 0)) {
+        aspect = Renderer::ASPECT_NATIVE;
+    } else if (string[0] == 's' && (strcmp(string, "square") == 0)) {
+        aspect = Renderer::ASPECT_SQUARE;
+    } else if (string[0] == 'w' && (strcmp(string, "window") == 0)) {
+        aspect = Renderer::ASPECT_WINDOW;
+    } else {
+        Tcl_AppendResult(interp, "bad camera aspect option \"", string,
+                         "\": should be native, square or window", (char*)NULL);
+        return TCL_ERROR;
+    }
+    g_renderer->setCameraAspect(aspect);
+    return TCL_OK;
+}
+
+static int
+CameraModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    Renderer::CameraMode mode;
+    const char *string = Tcl_GetString(objv[2]);
+    if (string[0] == 'p' && (strcmp(string, "persp") == 0)) {
+        mode = Renderer::PERSPECTIVE;
+    } else if (string[0] == 'o' && (strcmp(string, "ortho") == 0)) {
+        mode = Renderer::ORTHO;
+    } else if (string[0] == 'i' && (strcmp(string, "image") == 0)) {
+        mode = Renderer::IMAGE;
+    } else {
+        Tcl_AppendResult(interp, "bad camera mode option \"", string,
+                         "\": should be persp, ortho or image", (char*)NULL);
+        return TCL_ERROR;
+    }
+    g_renderer->setCameraMode(mode);
+    return TCL_OK;
+}
+
+static int
+CameraGetOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double pos[3];
+    double focalPt[3];
+    double viewUp[3];
+
+    g_renderer->getCameraOrientationAndPosition(pos, focalPt, viewUp);
+
+    char buf[256];
+    snprintf(buf, sizeof(buf), "nv>camera set %.12e %.12e %.12e %.12e %.12e %.12e %.12e %.12e %.12e\n", 
+             pos[0], pos[1], pos[2], focalPt[0], focalPt[1], focalPt[2], viewUp[0], viewUp[1], viewUp[2]);
+
+#ifdef USE_THREADS
+    queueResponse(clientData, buf, strlen(buf), Response::VOLATILE);
+#else 
+    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+#endif
+    return TCL_OK;
+}
+
+static int
+CameraOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double quat[4];
+
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->setCameraOrientation(quat);
+    return TCL_OK;
+}
+
+static int
+CameraOrthoOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    const char *string = Tcl_GetString(objv[2]);
+
+    if (string[0] == 'p' && strcmp(string, "pixel") == 0) {
+        int x, y, width, height;
+        if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
+            Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK ||
+            Tcl_GetIntFromObj(interp, objv[5], &width) != TCL_OK ||
+            Tcl_GetIntFromObj(interp, objv[6], &height) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (!g_renderer->setCameraZoomRegionPixels(x, y, width, height)) {
+            Tcl_AppendResult(interp, "Camera mode is not image", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else if (string[0] == 'w' && strcmp(string, "world") == 0) {
+        double x, y, width, height;
+        if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK ||
+            Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK ||
+            Tcl_GetDoubleFromObj(interp, objv[5], &width) != TCL_OK ||
+            Tcl_GetDoubleFromObj(interp, objv[6], &height) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (!g_renderer->setCameraZoomRegion(x, y, width, height)) {
+            Tcl_AppendResult(interp, "Camera mode is not image", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        Tcl_AppendResult(interp, "bad camera ortho option \"", string,
+                         "\": should be pixel or world", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+static int
+CameraPanOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double x, y;
+
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->panCamera(x, y);
+    return TCL_OK;
+}
+
+static int
+CameraResetOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *string = Tcl_GetString(objv[2]);
+        char c = string[0];
+        if ((c != 'a') || (strcmp(string, "all") != 0)) {
+            Tcl_AppendResult(interp, "bad camera reset option \"", string,
+                         "\": should be all", (char*)NULL);
+            return TCL_ERROR;
+        }
+        g_renderer->resetCamera(true);
+    } else {
+        g_renderer->resetCamera(false);
+    }
+    return TCL_OK;
+}
+
+static int
+CameraRotateOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double yaw, pitch, roll;
+
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &yaw) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pitch) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &roll) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->rotateCamera(yaw, pitch, roll);
+    return TCL_OK;
+}
+
+static int
+CameraSetOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double pos[3];
+    double focalPt[3];
+    double viewUp[3];
+
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &focalPt[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[6], &focalPt[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[7], &focalPt[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[8], &viewUp[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[9], &viewUp[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[10], &viewUp[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->setCameraOrientationAndPosition(pos, focalPt, viewUp);
+    return TCL_OK;
+}
+
+static int
+CameraZoomOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double z;
+
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &z) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->zoomCamera(z);
+    return TCL_OK;
+}
+
+static CmdSpec cameraOps[] = {
+    {"aspect",  1, CameraAspectOp, 3, 3, "aspect"},
+    {"get",     1, CameraGetOp, 2, 2, ""},
+    {"mode",    1, CameraModeOp, 3, 3, "mode"},
+    {"orient",  3, CameraOrientOp, 6, 6, "qw qx qy qz"},
+    {"ortho",   3, CameraOrthoOp, 7, 7, "coordMode x y width height"},
+    {"pan",     1, CameraPanOp, 4, 4, "panX panY"},
+    {"reset",   2, CameraResetOp, 2, 3, "?all?"},
+    {"rotate",  2, CameraRotateOp, 5, 5, "angle angle angle"},
+    {"set",     1, CameraSetOp, 11, 11, "posX posY posZ focalPtX focalPtY focalPtZ viewUpX viewUpY viewUpZ"},
+    {"zoom",    1, CameraZoomOp, 3, 3, "zoomAmount"}
+};
+static int nCameraOps = NumCmdSpecs(cameraOps);
+
+static int
+CameraCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nCameraOps, cameraOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ClientInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    Tcl_DString ds;
+    Tcl_Obj *objPtr, *listObjPtr, **items;
+    int numItems;
+    char buf[BUFSIZ];
+    const char *string;
+    int length;
+    int result;
+    static bool first = true;
+
+    /* Use the initial client key value pairs as the parts for a generating
+     * a unique file name. */
+    int fd = VtkVis::getStatsFile(objv[1]);
+    if (fd < 0) {
+	Tcl_AppendResult(interp, "can't open stats file: ", 
+                         Tcl_PosixError(interp), (char *)NULL);
+	return TCL_ERROR;
+    }
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
+    Tcl_IncrRefCount(listObjPtr);
+    if (first) {
+        first = false;
+        objPtr = Tcl_NewStringObj("render_start", 12);
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+        /* renderer */
+        objPtr = Tcl_NewStringObj("renderer", 8);
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+        objPtr = Tcl_NewStringObj("vtkvis", 6);
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+        /* pid */
+        objPtr = Tcl_NewStringObj("pid", 3);
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+        Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj((long)VtkVis::g_stats.pid));
+        /* host */
+        objPtr = Tcl_NewStringObj("host", 4);
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+        gethostname(buf, BUFSIZ-1);
+        buf[BUFSIZ-1] = '\0';
+        objPtr = Tcl_NewStringObj(buf, -1);
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    } else {
+        objPtr = Tcl_NewStringObj("render_info", 11);
+        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    /* date */
+    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4));
+    strcpy(buf, ctime(&VtkVis::g_stats.start.tv_sec));
+    buf[strlen(buf) - 1] = '\0';
+    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1));
+    /* date_secs */
+    Tcl_ListObjAppendElement(interp, listObjPtr, 
+                             Tcl_NewStringObj("date_secs", 9));
+    Tcl_ListObjAppendElement(interp, listObjPtr, 
+                             Tcl_NewLongObj(VtkVis::g_stats.start.tv_sec));
+    /* Client arguments. */
+    if (Tcl_ListObjGetElements(interp, objv[1], &numItems, &items) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    for (int i = 0; i < numItems; i++) {
+        Tcl_ListObjAppendElement(interp, listObjPtr, items[i]);
+    }
+    Tcl_DStringInit(&ds);
+    string = Tcl_GetStringFromObj(listObjPtr, &length);
+    Tcl_DStringAppend(&ds, string, length);
+    Tcl_DStringAppend(&ds, "\n", 1);
+    result = VtkVis::writeToStatsFile(fd, Tcl_DStringValue(&ds), 
+                                      Tcl_DStringLength(&ds));
+    Tcl_DStringFree(&ds);
+    Tcl_DecrRefCount(listObjPtr);
+    return result;
+}
+
+static int
+ColorMapAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[2]);
+    int cmapc, omapc;
+    Tcl_Obj **cmapv = NULL;
+    Tcl_Obj **omapv = NULL;
+
+    if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if ((cmapc % 4) != 0) {
+        Tcl_AppendResult(interp, "wrong # elements in colormap: should be ",
+                         "{ value r g b ... }", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    ColorMap *colorMap = new ColorMap(name);
+    colorMap->setNumberOfTableEntries(256);
+
+    for (int i = 0; i < cmapc; i += 4) {
+        double val[4];
+        for (int j = 0; j < 4; j++) {
+            if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &val[j]) != TCL_OK) {
+                delete colorMap;
+                return TCL_ERROR;
+            }
+            if ((val[j] < 0.0) || (val[j] > 1.0)) {
+                Tcl_AppendResult(interp, "bad colormap value \"",
+                                 Tcl_GetString(cmapv[i+j]),
+                                 "\": should be in the range [0,1]", (char*)NULL);
+                delete colorMap;
+                return TCL_ERROR;
+            }
+        }
+        ColorMap::ControlPoint cp;
+        cp.value = val[0];
+        for (int c = 0; c < 3; c++) {
+            cp.color[c] = val[c+1];
+        }
+        colorMap->addControlPoint(cp);
+    }
+
+    if (Tcl_ListObjGetElements(interp, objv[4], &omapc, &omapv) != TCL_OK) {
+        delete colorMap;
+        return TCL_ERROR;
+    }
+    if ((omapc % 2) != 0) {
+        Tcl_AppendResult(interp, "wrong # elements in opacitymap: should be ",
+                         "{ value alpha ... }", (char*)NULL);
+        delete colorMap;
+        return TCL_ERROR;
+    }
+    for (int i = 0; i < omapc; i += 2) {
+        double val[2];
+        for (int j = 0; j < 2; j++) {
+            if (Tcl_GetDoubleFromObj(interp, omapv[i+j], &val[j]) != TCL_OK) {
+                delete colorMap;
+                return TCL_ERROR;
+            }
+            if ((val[j] < 0.0) || (val[j] > 1.0)) {
+                Tcl_AppendResult(interp, "bad opacitymap value \"",
+                                 Tcl_GetString(omapv[i+j]),
+                                 "\": should be in the range [0,1]", (char*)NULL);
+                delete colorMap;
+                return TCL_ERROR;
+            }
+        }
+        ColorMap::OpacityControlPoint ocp;
+        ocp.value = val[0];
+        ocp.alpha = val[1];
+        colorMap->addOpacityControlPoint(ocp);
+    }
+
+    colorMap->build();
+    g_renderer->addColorMap(name, colorMap);
+    return TCL_OK;
+}
+
+static int
+ColorMapAddSplineOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[2]);
+    bool doSpline = false;
+    int nextArg = 3;
+    if (objc > 5) {
+        const char *arg = Tcl_GetString(objv[nextArg]);
+        if (arg[0] == '-') {
+            if (strcmp(arg, "-spline") == 0) {
+                doSpline = true;
+                nextArg++;
+            } else {
+                Tcl_AppendResult(interp, "unknown option \"", arg, "\"",
+                                 (char *)NULL);
+                return TCL_ERROR;
+            }
+        }
+    }
+    int cmapc, omapc;
+    Tcl_Obj **cmapv = NULL;
+    Tcl_Obj **omapv = NULL;
+
+    if (Tcl_ListObjGetElements(interp, objv[nextArg++], &cmapc, &cmapv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (doSpline) {
+        if ((cmapc % 6) != 0) {
+            Tcl_AppendResult(interp, "wrong # elements in colormap: should be ",
+                             "{ value r g b midpoint sharpness ... }", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if ((cmapc % 4) != 0) {
+            Tcl_AppendResult(interp, "wrong # elements in colormap: should be ",
+                             "{ value r g b ... }", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+
+    ColorMap *colorMap = new ColorMap(name);
+    colorMap->setNumberOfTableEntries(256);
+
+    int numVals = doSpline ? 6 : 4;
+    for (int i = 0; i < cmapc; i += numVals) {
+        double val[numVals];
+        for (int j = 0; j < numVals; j++) {
+            if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &val[j]) != TCL_OK) {
+                delete colorMap;
+                return TCL_ERROR;
+            }
+            if ((val[j] < 0.0) || (val[j] > 1.0)) {
+                Tcl_AppendResult(interp, "bad colormap value \"",
+                                 Tcl_GetString(cmapv[i+j]),
+                                 "\": should be in the range [0,1]", (char*)NULL);
+                delete colorMap;
+                return TCL_ERROR;
+            }
+        }
+        ColorMap::ControlPoint cp;
+        cp.value = val[0];
+        for (int c = 0; c < 3; c++) {
+            cp.color[c] = val[c+1];
+        }
+        if (numVals > 4) {
+            cp.midpoint = val[4];
+            cp.sharpness = val[5];
+        }
+        colorMap->addControlPoint(cp);
+    }
+    doSpline = false;
+    if (objc > 5) {
+        const char *arg = Tcl_GetString(objv[nextArg]);
+        if (arg[0] == '-') {
+            if (strcmp(arg, "-spline") == 0) {
+                doSpline = true;
+                nextArg++;
+            } else {
+                Tcl_AppendResult(interp, "unknown option \"", arg, "\"",
+                                 (char *)NULL);
+                return TCL_ERROR;
+            }
+        }
+    }
+    if (Tcl_ListObjGetElements(interp, objv[nextArg++], &omapc, &omapv) != TCL_OK) {
+        delete colorMap;
+        return TCL_ERROR;
+    }
+    if (doSpline) {
+        if ((omapc % 4) != 0) {
+            Tcl_AppendResult(interp, "wrong # elements in opacitymap: should be ",
+                             "{ value alpha midpoint sharpness ... }", (char*)NULL);
+            delete colorMap;
+            return TCL_ERROR;
+        }
+    } else {
+        if ((omapc % 2) != 0) {
+            Tcl_AppendResult(interp, "wrong # elements in opacitymap: should be ",
+                             "{ value alpha ... }", (char*)NULL);
+            delete colorMap;
+            return TCL_ERROR;
+        }
+    }
+    numVals = doSpline ? 4 : 2;
+    for (int i = 0; i < omapc; i += numVals) {
+        double val[numVals];
+        for (int j = 0; j < numVals; j++) {
+            if (Tcl_GetDoubleFromObj(interp, omapv[i+j], &val[j]) != TCL_OK) {
+                delete colorMap;
+                return TCL_ERROR;
+            }
+            if ((val[j] < 0.0) || (val[j] > 1.0)) {
+                Tcl_AppendResult(interp, "bad opacitymap value \"",
+                                 Tcl_GetString(omapv[i+j]),
+                                 "\": should be in the range [0,1]", (char*)NULL);
+                delete colorMap;
+                return TCL_ERROR;
+            }
+        }
+        ColorMap::OpacityControlPoint ocp;
+        ocp.value = val[0];
+        ocp.alpha = val[1];
+        if (numVals > 2) {
+            ocp.midpoint = val[2];
+            ocp.sharpness = val[3];
+        }
+        colorMap->addOpacityControlPoint(ocp);
+    }
+
+    colorMap->build();
+    g_renderer->addColorMap(name, colorMap);
+    return TCL_OK;
+}
+
+static int
+ColorMapDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteColorMap(name);
+    } else {
+        g_renderer->deleteColorMap("all");
+    }
+    return TCL_OK;
+}
+
+static int
+ColorMapNumTableEntriesOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    int numEntries;
+    if (Tcl_GetIntFromObj(interp, objv[2], &numEntries) != TCL_OK) {
+        const char *str = Tcl_GetString(objv[2]);
+        if (str[0] == 'd' && strcmp(str, "default") == 0) {
+            numEntries = -1;
+        } else {
+            Tcl_AppendResult(interp, "bad colormap resolution value \"", str,
+                             "\": should be a positive integer or \"default\"", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else if (numEntries < 1) {
+        Tcl_AppendResult(interp, "bad colormap resolution value \"", Tcl_GetString(objv[2]),
+                         "\": should be a positive integer or \"default\"", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+
+        g_renderer->setColorMapNumberOfTableEntries(name, numEntries);
+    } else {
+        g_renderer->setColorMapNumberOfTableEntries("all", numEntries);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec colorMapOps[] = {
+    {"add",    1, ColorMapAddOp,             5, 5, "colorMapName colormap alphamap"},
+    {"define", 3, ColorMapAddSplineOp,       5, 7, "colorMapName ?-spline? colormap ?-spline? alphamap"},
+    {"delete", 3, ColorMapDeleteOp,          2, 3, "?colorMapName?"},
+    {"res",    1, ColorMapNumTableEntriesOp, 3, 4, "numTableEntries ?colorMapName?"}
+};
+static int nColorMapOps = NumCmdSpecs(colorMapOps);
+
+static int
+ColorMapCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nColorMapOps, colorMapOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ConeAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    double radius, height;
+    bool cap;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &radius) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &height) != TCL_OK ||
+        GetBooleanFromObj(interp, objv[4], &cap) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[5]);
+    if (!g_renderer->addCone(name, radius, height, cap)) {
+        Tcl_AppendResult(interp, "Failed to create cone", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+ConeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Cone>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Cone>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+ConeColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Cone>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Cone>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Cone>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Cone>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Cone>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Cone>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Cone>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Cone>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Cone>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Cone>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Cone>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Cone>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Cone>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Cone>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Cone>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Cone>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Cone>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Cone>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Cone>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Cone>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Cone>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Cone>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Cone>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Cone>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Cone>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Cone>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+ConePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Cone>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Cone>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeResolutionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    int res;
+    if (Tcl_GetIntFromObj(interp, objv[2], &res) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setConeResolution(name, res);
+    } else {
+        g_renderer->setConeResolution("all", res);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Cone>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Cone>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Cone>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Cone>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Cone>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Cone>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ConeWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Cone>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Cone>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec coneOps[] = {
+    {"add",       1, ConeAddOp, 6, 6, "radius height cap name"},
+    {"color",     2, ConeColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, ConeCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, ConeDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, ConeEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, ConeFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, ConeLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, ConeLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, ConeLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, ConeMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, ConeOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, ConeOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, ConeOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, ConePositionOp, 5, 6, "x y z ?name?"},
+    {"resolution",1, ConeResolutionOp, 3, 4, "res ?name?"},
+    {"scale",     2, ConeScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shading",   2, ConeShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, ConeVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, ConeWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nConeOps = NumCmdSpecs(coneOps);
+
+static int
+ConeCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nConeOps, coneOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+Contour2DAddContourListOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    std::vector<double> contourList;
+
+    int clistc;
+    Tcl_Obj **clistv;
+
+    if (Tcl_ListObjGetElements(interp, objv[3], &clistc, &clistv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    for (int i = 0; i < clistc; i++) {
+        double val;
+        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        contourList.push_back(val);
+    }
+
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        if (!g_renderer->addContour2D(name, contourList)) {
+            Tcl_AppendResult(interp, "Failed to create contour2d", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addContour2D("all", contourList)) {
+            Tcl_AppendResult(interp, "Failed to create contour2d for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DAddNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    int numContours;
+    if (Tcl_GetIntFromObj(interp, objv[3], &numContours) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        if (!g_renderer->addContour2D(name, numContours)) {
+            Tcl_AppendResult(interp, "Failed to create contour2d", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+       if (!g_renderer->addContour2D("all", numContours)) {
+            Tcl_AppendResult(interp, "Failed to create contour2d for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+       }
+    }
+    return TCL_OK;
+}
+
+static CmdSpec contour2dAddOps[] = {
+    {"contourlist", 1, Contour2DAddContourListOp, 4, 5, "contourList ?dataSetName?"},
+    {"numcontours", 1, Contour2DAddNumContoursOp, 4, 5, "numContours ?dataSetName?"}
+};
+static int nContour2dAddOps = NumCmdSpecs(contour2dAddOps);
+
+static int
+Contour2DAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nContour2dAddOps, contour2dAddOps,
+                        CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+Contour2DDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Contour2D>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Contour2D>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Contour2D>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Contour2D>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    Contour2D::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = Contour2D::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Contour2D::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Contour2D::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = Contour2D::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = Contour2D::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = Contour2D::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == Contour2D::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setContour2DColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setContour2DColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DContourListOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    std::vector<double> contourList;
+
+    int clistc;
+    Tcl_Obj **clistv;
+
+    if (Tcl_ListObjGetElements(interp, objv[2], &clistc, &clistv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    for (int i = 0; i < clistc; i++) {
+        double val;
+        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        contourList.push_back(val);
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setContour2DContourList(name, contourList);
+    } else {
+        g_renderer->setContour2DContourList("all", contourList);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Contour2D>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Contour2D>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Contour2D>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Contour2D>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Contour2D>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Contour2D>("all", width);
+    }
+    return TCL_OK;
+}
+
+
+static int
+Contour2DNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    int numContours;
+
+    if (Tcl_GetIntFromObj(interp, objv[2], &numContours) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setContour2DNumContours(name, numContours);
+    } else {
+        g_renderer->setContour2DNumContours("all", numContours);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Contour2D>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Contour2D>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Contour2D>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Contour2D>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Contour2D>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Contour2D>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Contour2D>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Contour2D>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour2DVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Contour2D>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Contour2D>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec contour2dOps[] = {
+    {"add",          1, Contour2DAddOp, 4, 5, "oper value ?dataSetName?"},
+    {"ccolor",       2, Contour2DLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"color",        5, Contour2DLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, Contour2DColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, Contour2DColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"contourlist",  3, Contour2DContourListOp, 3, 4, "contourList ?dataSetName?"},
+    {"delete",       1, Contour2DDeleteOp, 2, 3, "?dataSetName?"},
+    {"lighting",     3, Contour2DLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, Contour2DLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, Contour2DLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"numcontours",  1, Contour2DNumContoursOp, 3, 4, "numContours ?dataSetName?"},
+    {"opacity",      2, Contour2DOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, Contour2DOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          1, Contour2DPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"scale",        1, Contour2DScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"visible",      1, Contour2DVisibleOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nContour2dOps = NumCmdSpecs(contour2dOps);
+
+static int
+Contour2DCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nContour2dOps, contour2dOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+Contour3DAddContourListOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    std::vector<double> contourList;
+
+    int clistc;
+    Tcl_Obj **clistv;
+
+    if (Tcl_ListObjGetElements(interp, objv[3], &clistc, &clistv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    for (int i = 0; i < clistc; i++) {
+        double val;
+        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        contourList.push_back(val);
+    }
+
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        if (!g_renderer->addContour3D(name, contourList)) {
+            Tcl_AppendResult(interp, "Failed to create contour3d", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addContour3D("all", contourList)) {
+            Tcl_AppendResult(interp, "Failed to create contour3d for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DAddNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    int numContours;
+    if (Tcl_GetIntFromObj(interp, objv[3], &numContours) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        if (!g_renderer->addContour3D(name, numContours)) {
+            Tcl_AppendResult(interp, "Failed to create contour3d", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addContour3D("all", numContours)) {
+            Tcl_AppendResult(interp, "Failed to create contour3d for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static CmdSpec contour3dAddOps[] = {
+    {"contourlist", 1, Contour3DAddContourListOp, 4, 5, "contourList ?dataSetName?"},
+    {"numcontours", 1, Contour3DAddNumContoursOp, 4, 5, "numContours ?dataSetName?"}
+};
+static int nContour3dAddOps = NumCmdSpecs(contour3dAddOps);
+
+static int
+Contour3DAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nContour3dAddOps, contour3dAddOps,
+                        CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+Contour3DColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Contour3D>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Contour3D>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Contour3D>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Contour3D>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    Contour3D::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = Contour3D::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Contour3D::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Contour3D::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = Contour3D::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = Contour3D::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = Contour3D::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == Contour3D::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setContour3DColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setContour3DColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DContourFieldOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    const char *fieldName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setContour3DContourField(dataSetName, fieldName);
+    } else {
+        g_renderer->setContour3DContourField("all", fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DContourListOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    std::vector<double> contourList;
+
+    int clistc;
+    Tcl_Obj **clistv;
+
+    if (Tcl_ListObjGetElements(interp, objv[2], &clistc, &clistv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    for (int i = 0; i < clistc; i++) {
+        double val;
+        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        contourList.push_back(val);
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setContour3DContourList(name, contourList);
+    } else {
+        g_renderer->setContour3DContourList("all", contourList);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Contour3D>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Contour3D>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Contour3D>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Contour3D>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Contour3D>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Contour3D>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Contour3D>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Contour3D>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Contour3D>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Contour3D>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    int numContours;
+
+    if (Tcl_GetIntFromObj(interp, objv[2], &numContours) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setContour3DNumContours(name, numContours);
+    } else {
+        g_renderer->setContour3DNumContours("all", numContours);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Contour3D>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Contour3D>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Contour3D>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Contour3D>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Contour3D>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Contour3D>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Contour3D>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Contour3D>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Contour3D>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Contour3D>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Contour3DWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Contour3D>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Contour3D>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec contour3dOps[] = {
+    {"add",          1, Contour3DAddOp, 4, 5, "oper value ?dataSetName?"},
+    {"ccolor",       2, Contour3DColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"color",        5, Contour3DColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, Contour3DColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, Contour3DColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"contourfield", 8, Contour3DContourFieldOp, 3, 4, "fieldName ?dataSetName?"},
+    {"contourlist",  8, Contour3DContourListOp, 3, 4, "contourList ?dataSetName?"},
+    {"delete",       1, Contour3DDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, Contour3DEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"lighting",     3, Contour3DLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, Contour3DLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, Contour3DLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"numcontours",  1, Contour3DNumContoursOp, 3, 4, "numContours ?dataSetName?"},
+    {"opacity",      2, Contour3DOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, Contour3DOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          1, Contour3DPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"scale",        1, Contour3DScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"visible",      1, Contour3DVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"wireframe",    1, Contour3DWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nContour3dOps = NumCmdSpecs(contour3dOps);
+
+static int
+Contour3DCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nContour3dOps, contour3dOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+CutplaneAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<Cutplane>(name)) {
+            Tcl_AppendResult(interp, "Failed to create cutplane", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<Cutplane>("all")) {
+            Tcl_AppendResult(interp, "Failed to create cutplane for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneCloudStyleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    Cutplane::CloudStyle style;
+    char *str =  Tcl_GetString(objv[2]);
+    if (str[0] == 'm' && strcmp(str, "mesh") == 0) {
+        style = Cutplane::CLOUD_MESH;
+    } else if (str[0] == 's' && strcmp(str, "splat") == 0) {
+        style = Cutplane::CLOUD_SPLAT;
+    } else {
+        Tcl_AppendResult(interp, "bad cloudstyle option \"", str,
+                         "\": should be one of: 'mesh', 'splat'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setCutplaneCloudStyle(name, style);
+    } else {
+        g_renderer->setCutplaneCloudStyle("all", style);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Cutplane>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Cutplane>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Cutplane>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Cutplane>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    Cutplane::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Cutplane::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Cutplane::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = Cutplane::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = Cutplane::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = Cutplane::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setCutplaneColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setCutplaneColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Cutplane>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Cutplane>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Cutplane>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Cutplane>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Cutplane>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Cutplane>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Cutplane>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Cutplane>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Cutplane>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Cutplane>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Cutplane>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Cutplane>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Cutplane>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Cutplane>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setCutplaneOutlineVisibility(name, state);
+    } else {
+        g_renderer->setCutplaneOutlineVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplanePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Cutplane>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Cutplane>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplanePreInterpOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<Cutplane>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<Cutplane>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Cutplane>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Cutplane>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneSliceVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Axis axis;
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        axis = X_AXIS;
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        axis = Y_AXIS;
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        axis = Z_AXIS;
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName bool", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setCutplaneSliceVisibility(name, axis, state);
+    } else {
+        g_renderer->setCutplaneSliceVisibility("all", axis, state);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Cutplane>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Cutplane>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneVolumeSliceOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double ratio;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Axis axis;
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        axis = X_AXIS;
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        axis = Y_AXIS;
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        axis = Z_AXIS;
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName ratio", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGraphicsObjectVolumeSlice<Cutplane>(name, axis, ratio);
+    } else {
+        g_renderer->setGraphicsObjectVolumeSlice<Cutplane>("all", axis, ratio);
+    }
+    return TCL_OK;
+}
+
+static int
+CutplaneWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Cutplane>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Cutplane>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec cutplaneOps[] = {
+    {"add",          2, CutplaneAddOp, 2, 3, "?dataSetName?"},
+    {"axis",         2, CutplaneSliceVisibilityOp, 4, 5, "axis bool ?dataSetName?"},
+    {"ccolor",       2, CutplaneColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"cloudstyle",   2, CutplaneCloudStyleOp, 3, 4, "style ?dataSetName?"},
+    {"color",        5, CutplaneColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, CutplaneColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, CutplaneColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"delete",       1, CutplaneDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, CutplaneEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"lighting",     3, CutplaneLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, CutplaneLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, CutplaneLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"opacity",      2, CutplaneOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, CutplaneOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"outline",      2, CutplaneOutlineOp, 3, 4, "bool ?dataSetName?"},
+    {"pos",          2, CutplanePositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"preinterp",    2, CutplanePreInterpOp, 3, 4, "bool ?dataSetName?"},
+    {"scale",        2, CutplaneScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"slice",        2, CutplaneVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"},
+    {"visible",      1, CutplaneVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"wireframe",    1, CutplaneWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nCutplaneOps = NumCmdSpecs(cutplaneOps);
+
+static int
+CutplaneCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nCutplaneOps, cutplaneOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+CylinderAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double radius, height;
+    bool cap = true;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &radius) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &height) != TCL_OK ||
+        GetBooleanFromObj(interp, objv[4], &cap) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[5]);
+    if (!g_renderer->addCylinder(name, radius, height, cap)) {
+        Tcl_AppendResult(interp, "Failed to create cylinder", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Cylinder>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Cylinder>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Cylinder>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Cylinder>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Cylinder>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Cylinder>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Cylinder>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Cylinder>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Cylinder>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Cylinder>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Cylinder>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Cylinder>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Cylinder>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Cylinder>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Cylinder>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Cylinder>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Cylinder>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Cylinder>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Cylinder>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Cylinder>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Cylinder>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Cylinder>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Cylinder>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Cylinder>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Cylinder>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Cylinder>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Cylinder>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Cylinder>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Cylinder>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Cylinder>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderResolutionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    int res;
+    if (Tcl_GetIntFromObj(interp, objv[2], &res) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setCylinderResolution(name, res);
+    } else {
+        g_renderer->setCylinderResolution("all", res);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Cylinder>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Cylinder>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Cylinder>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Cylinder>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Cylinder>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Cylinder>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+CylinderWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Cylinder>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Cylinder>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec cylinderOps[] = {
+    {"add",       1, CylinderAddOp, 6, 6, "radius height cap name"},
+    {"color",     2, CylinderColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, CylinderCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, CylinderDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, CylinderEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, CylinderFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, CylinderLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, CylinderLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, CylinderLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, CylinderMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, CylinderOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, CylinderOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, CylinderOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, CylinderPositionOp, 5, 6, "x y z ?name?"},
+    {"resolution",1, CylinderResolutionOp, 3, 4, "res ?name?"},
+    {"scale",     2, CylinderScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shading",   2, CylinderShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, CylinderVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, CylinderWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nCylinderOps = NumCmdSpecs(cylinderOps);
+
+static int
+CylinderCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nCylinderOps, cylinderOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+DataSetActiveScalarsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    const char *scalarName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        if (!g_renderer->setDataSetActiveScalars(name, scalarName)) {
+            Tcl_AppendResult(interp, "scalar \"", scalarName,
+                         "\" not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->setDataSetActiveScalars("all", scalarName)) {
+            Tcl_AppendResult(interp, "scalar \"", scalarName,
+                         "\" not found in one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+DataSetActiveVectorsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    const char *vectorName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        if (!g_renderer->setDataSetActiveVectors(name, vectorName)) {
+            Tcl_AppendResult(interp, "vector \"", vectorName,
+                         "\" not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->setDataSetActiveVectors("all", vectorName)) {
+            Tcl_AppendResult(interp, "vector \"", vectorName,
+                         "\" not found in one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+DataSetAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[2]);
+    const char *string = Tcl_GetString(objv[3]);
+    char c = string[0];
+    if ((c != 'd') || (strcmp(string, "data") != 0)) {
+        Tcl_AppendResult(interp, "bad dataset option \"", string,
+                         "\": should be data", (char*)NULL);
+        return TCL_ERROR;
+    }
+    string = Tcl_GetString(objv[4]);
+    c = string[0];
+    if ((c != 'f') || (strcmp(string, "follows") != 0)) {
+        Tcl_AppendResult(interp, "bad dataset option \"", string,
+                         "\": should be follows", (char*)NULL);
+        return TCL_ERROR;
+    }
+    int nbytes = 0;
+    if (Tcl_GetIntFromObj(interp, objv[5], &nbytes) != TCL_OK ||
+        nbytes < 0) {
+        return TCL_ERROR;
+    }
+    char *data = (char *)malloc(nbytes);
+    if (!SocketRead(data, nbytes)) {
+        free(data);
+        return TCL_ERROR;
+    }
+    g_renderer->addDataSet(name);
+    if (!g_renderer->setData(name, data, nbytes)) {
+        free(data);
+        g_renderer->deleteDataSet(name);
+        USER_ERROR("Failed to load data for dataset \"%s\"", name);
+        return TCL_ERROR;
+    }
+    g_stats.nDataSets++;
+    g_stats.nDataBytes += nbytes;
+    free(data);
+    return TCL_OK;
+}
+
+static int
+DataSetDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        TRACE("Deleting dataset %s", name);
+        g_renderer->deleteDataSet(name);
+    } else {
+        g_renderer->deleteDataSet("all");
+    }
+    return TCL_OK;
+}
+
+static int
+DataSetGetScalarPixelOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[5]);
+    int x, y;
+    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double value;
+    if (!g_renderer->getScalarValueAtPixel(name, x, y, &value)) {
+        Tcl_AppendResult(interp, "Pixel out of dataset bounds or no scalar data available", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    char buf[256];
+    int length;
+
+    length = snprintf(buf, sizeof(buf), "nv>dataset scalar pixel %d %d %g %s\n",
+                      x, y, value, name);
+
+#ifdef USE_THREADS
+    queueResponse(clientData, buf, length, Response::VOLATILE);
+#else
+    ssize_t bytesWritten = SocketWrite(buf, length);
+
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+#endif
+    return TCL_OK;
+}
+
+static int
+DataSetGetScalarWorldOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[6]);
+    double x, y, z;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &z) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double value;
+    if (!g_renderer->getScalarValue(name, x, y, z, &value)) {
+        Tcl_AppendResult(interp, "Coordinate out of dataset bounds or no scalar data available", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    char buf[256];
+    int length;
+
+    length = snprintf(buf, sizeof(buf), 
+                      "nv>dataset scalar world %g %g %g %g %s\n",
+                      x, y, z, value, name);
+
+#ifdef USE_THREADS
+    queueResponse(clientData, buf, length, Response::VOLATILE);
+#else 
+    ssize_t bytesWritten = SocketWrite(buf, length);
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+#endif
+    return TCL_OK;
+}
+
+static CmdSpec dataSetGetScalarOps[] = {
+    {"pixel", 1, DataSetGetScalarPixelOp, 6, 6, "x y dataSetName"},
+    {"world", 1, DataSetGetScalarWorldOp, 7, 7, "x y z dataSetName"}
+};
+static int nDataSetGetScalarOps = NumCmdSpecs(dataSetGetScalarOps);
+
+static int
+DataSetGetScalarOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nDataSetGetScalarOps, dataSetGetScalarOps,
+                        CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+DataSetGetVectorPixelOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[5]);
+    int x, y;
+    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double value[3];
+    if (!g_renderer->getVectorValueAtPixel(name, x, y, value)) {
+        Tcl_AppendResult(interp, "Pixel out of dataset bounds or no vector data available", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    char buf[256];
+    int length; 
+    length = snprintf(buf, sizeof(buf), 
+                      "nv>dataset vector pixel %d %d %g %g %g %s\n",
+                      x, y,
+                      value[0], value[1], value[2], name);
+
+#ifdef USE_THREADS
+    queueResponse(clientData, buf, length, Response::VOLATILE);
+#else 
+    ssize_t bytesWritten = SocketWrite(buf, length);
+
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+#endif /*USE_THREADS*/
+    return TCL_OK;
+}
+
+static int
+DataSetGetVectorWorldOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    const char *name = Tcl_GetString(objv[6]);
+    double x, y, z;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &z) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    double value[3];
+    if (!g_renderer->getVectorValue(name, x, y, z, value)) {
+        Tcl_AppendResult(interp, "Coordinate out of dataset bounds or no vector data available", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    char buf[256];
+    int length;
+    length = snprintf(buf, sizeof(buf), 
+                      "nv>dataset vector world %g %g %g %g %g %g %s\n",
+                      x, y, z,
+                      value[0], value[1], value[2], name);
+#ifdef USE_THREADS
+    queueResponse(clientData, buf, length, Response::VOLATILE);
+#else 
+    ssize_t bytesWritten = SocketWrite(buf, length);
+
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+#endif /*USE_THREADS*/
+    return TCL_OK;
+}
+
+static CmdSpec dataSetGetVectorOps[] = {
+    {"pixel", 1, DataSetGetVectorPixelOp, 6, 6, "x y dataSetName"},
+    {"world", 1, DataSetGetVectorWorldOp, 7, 7, "x y z dataSetName"}
+};
+static int nDataSetGetVectorOps = NumCmdSpecs(dataSetGetVectorOps);
+
+static int
+DataSetGetVectorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nDataSetGetVectorOps, dataSetGetVectorOps,
+                        CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+DataSetMapRangeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    const char *value = Tcl_GetString(objv[2]);
+    if (value[0] == 'a' && strcmp(value, "all") == 0) {
+        g_renderer->setUseCumulativeDataRange(true);
+    } else if (value[0] == 'e' && strcmp(value, "explicit") == 0) {
+        if (objc < 6 || objc > 9) {
+            Tcl_AppendResult(interp, "wrong number of arguments for explicit maprange", (char*)NULL);
+            return TCL_ERROR;
+        }
+        double range[2];
+        if (Tcl_GetDoubleFromObj(interp, objv[3], &range[0]) != TCL_OK ||
+            Tcl_GetDoubleFromObj(interp, objv[4], &range[1]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        const char *fieldName = Tcl_GetString(objv[5]);
+
+        DataSet::DataAttributeType type = DataSet::POINT_DATA;
+        int numComponents = 1;
+        int component = -1;
+        if (objc > 6) {
+            const char *fieldType = Tcl_GetString(objv[6]);
+            if (fieldType[0] == 'p' && strcmp(fieldType, "point_data") == 0) {
+                type = DataSet::POINT_DATA;
+            } else if (fieldType[0] == 'c' && strcmp(fieldType, "cell_data") == 0) {
+                type = DataSet::CELL_DATA;
+            } else if (fieldType[0] == 'f' && strcmp(fieldType, "field_data") == 0) {
+                type = DataSet::FIELD_DATA;
+            } else {
+                Tcl_AppendResult(interp, "bad field type option \"", fieldType,
+                                 "\": should be point_data, cell_data or field_data", (char*)NULL);
+                return TCL_ERROR;
+            }
+            if (objc > 7) {
+                if (Tcl_GetIntFromObj(interp, objv[7], &numComponents) != TCL_OK) {
+                    return TCL_ERROR;
+                }
+                if (objc == 9) {
+                    if (Tcl_GetIntFromObj(interp, objv[8], &component) != TCL_OK) {
+                        return TCL_ERROR;
+                    }
+                }
+            }
+        }
+        g_renderer->setCumulativeDataRange(range, fieldName, type, numComponents, component);
+    } else if (value[0] == 's' && strcmp(value, "separate") == 0) {
+        g_renderer->setUseCumulativeDataRange(false);
+    } else if (value[0] == 'v' && strcmp(value, "visible") == 0) {
+        g_renderer->setUseCumulativeDataRange(true, true);
+    } else {
+        Tcl_AppendResult(interp, "bad maprange option \"", value,
+                         "\": should be all, explicit, separate or visible", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+DataSetNamesOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    std::vector<std::string> dataSets;
+    g_renderer->getDataSetNames(dataSets);
+    std::ostringstream oss;
+    size_t len = 0;
+    oss << "nv>dataset names {";
+    len += 18;
+    for (size_t i = 0; i < dataSets.size(); i++) {
+        oss << "\"" << dataSets[i] << "\"";
+        len += 2 + dataSets[i].length();
+        if (i < dataSets.size() - 1) {
+            oss << " ";
+            len++;
+        }
+    }
+    oss << "}\n";
+    len += 2;
+#ifdef USE_THREADS
+    queueResponse(clientData, oss.str().c_str(), len, Response::VOLATILE);
+#else 
+    ssize_t bytesWritten = SocketWrite(oss.str().c_str(), len);
+
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+#endif /*USE_THREADS*/
+    return TCL_OK;
+}
+
+static int
+DataSetOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setDataSetOpacity(name, opacity);
+    } else {
+        g_renderer->setDataSetOpacity("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+DataSetVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setDataSetVisibility(name, state);
+    } else {
+        g_renderer->setDataSetVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec dataSetOps[] = {
+    {"add",       1, DataSetAddOp, 6, 6, "name data follows nBytes"},
+    {"delete",    1, DataSetDeleteOp, 2, 3, "?name?"},
+    {"getscalar", 4, DataSetGetScalarOp, 6, 7, "oper x y ?z? name"},
+    {"getvector", 4, DataSetGetVectorOp, 6, 7, "oper x y ?z? name"},
+    {"maprange",  1, DataSetMapRangeOp, 3, 9, "value ?min? ?max? ?fieldName? ?fieldType? ?fieldNumComp? ?compIdx?"},
+    {"names",     1, DataSetNamesOp, 2, 2, ""},
+    {"opacity",   2, DataSetOpacityOp, 3, 4, "value ?name?"},
+    {"scalar",    1, DataSetActiveScalarsOp, 3, 4, "scalarName ?name?"},
+    {"vector",    2, DataSetActiveVectorsOp, 3, 4, "vectorName ?name?"},
+    {"visible",   2, DataSetVisibleOp, 3, 4, "bool ?name?"}
+};
+static int nDataSetOps = NumCmdSpecs(dataSetOps);
+
+static int
+DataSetCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nDataSetOps, dataSetOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+DiskAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    double innerRadius, outerRadius;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &innerRadius) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &outerRadius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[4]);
+    if (!g_renderer->addDisk(name, innerRadius, outerRadius)) {
+        Tcl_AppendResult(interp, "Failed to create disk", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+DiskDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Disk>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Disk>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+DiskColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Disk>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Disk>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Disk>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Disk>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Disk>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Disk>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Disk>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Disk>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Disk>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Disk>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Disk>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Disk>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Disk>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Disk>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Disk>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Disk>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Disk>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Disk>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Disk>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Disk>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Disk>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Disk>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Disk>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Disk>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Disk>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Disk>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Disk>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Disk>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskResolutionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    int radial, circum;
+    if (Tcl_GetIntFromObj(interp, objv[2], &radial) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &circum) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setDiskResolution(name, radial, circum);
+    } else {
+        g_renderer->setDiskResolution("all", radial, circum);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Disk>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Disk>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Disk>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Disk>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Disk>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Disk>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+DiskWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Disk>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Disk>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec diskOps[] = {
+    {"add",       1, DiskAddOp, 5, 5, "innerRadius outerRadius name"},
+    {"color",     2, DiskColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, DiskCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, DiskDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, DiskEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, DiskFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, DiskLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, DiskLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, DiskLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, DiskMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, DiskOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, DiskOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, DiskOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, DiskPositionOp, 5, 6, "x y z ?name?"},
+    {"resolution",1, DiskResolutionOp, 4, 5, "resRadial resCircum ?name?"},
+    {"scale",     2, DiskScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shading",   2, DiskShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, DiskVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, DiskWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nDiskOps = NumCmdSpecs(diskOps);
+
+static int
+DiskCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+        Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nDiskOps, diskOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+GlyphsAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    Glyphs::GlyphShape shape;
+
+    const char *shapeOpt = Tcl_GetString(objv[2]);
+    if (shapeOpt[0] == 'a' && strcmp(shapeOpt, "arrow") == 0) {
+        shape = Glyphs::ARROW;
+    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cone") == 0) {
+        shape = Glyphs::CONE;
+    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cube") == 0) {
+        shape = Glyphs::CUBE;
+    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cylinder") == 0) {
+        shape = Glyphs::CYLINDER;
+    } else if (shapeOpt[0] == 'd' && strcmp(shapeOpt, "dodecahedron") == 0) {
+        shape = Glyphs::DODECAHEDRON;
+    } else if (shapeOpt[0] == 'i' && strcmp(shapeOpt, "icosahedron") == 0) {
+        shape = Glyphs::ICOSAHEDRON;
+    } else if (shapeOpt[0] == 'l' && strcmp(shapeOpt, "line") == 0) {
+        shape = Glyphs::LINE;
+    } else if (shapeOpt[0] == 'o' && strcmp(shapeOpt, "octahedron") == 0) {
+        shape = Glyphs::OCTAHEDRON;
+    } else if (shapeOpt[0] == 'p' && strcmp(shapeOpt, "point") == 0) {
+        shape = Glyphs::POINT;
+    } else if (shapeOpt[0] == 's' && strcmp(shapeOpt, "sphere") == 0) {
+        shape = Glyphs::SPHERE;
+    } else if (shapeOpt[0] == 't' && strcmp(shapeOpt, "tetrahedron") == 0) {
+        shape = Glyphs::TETRAHEDRON;
+    } else {
+        Tcl_AppendResult(interp, "bad shape option \"", shapeOpt,
+                         "\": should be one of: 'arrow', 'cone', 'cube', 'cylinder', 'dodecahedron', 'icosahedron', 'octahedron', 'point', 'sphere', 'tetrahedron'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        if (!g_renderer->addGlyphs(name, shape)) {
+            Tcl_AppendResult(interp, "Failed to create glyphs", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGlyphs("all", shape)) {
+            Tcl_AppendResult(interp, "Failed to create glyphs for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Glyphs>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Glyphs>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Glyphs>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Glyphs>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    Glyphs::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = Glyphs::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Glyphs::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Glyphs::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = Glyphs::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = Glyphs::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = Glyphs::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == Glyphs::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGlyphsColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setGlyphsColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Glyphs>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Glyphs>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Glyphs>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Glyphs>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Glyphs>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Glyphs>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Glyphs>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Glyphs>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Glyphs>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Glyphs>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsMaxNumberOfGlyphsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    int max;
+    bool random = true;
+    int offset = 0;
+    int ratio = 1;
+    if (Tcl_GetIntFromObj(interp, objv[2], &max) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc > 3) {
+        if (GetBooleanFromObj(interp, objv[3], &random) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetIntFromObj(interp, objv[4], &offset) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetIntFromObj(interp, objv[5], &ratio) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGlyphsMaximumNumberOfGlyphs(name, max, random, offset, ratio);
+    } else {
+        g_renderer->setGlyphsMaximumNumberOfGlyphs("all", max, random, offset, ratio);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsNormalizeScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool normalize;
+    if (GetBooleanFromObj(interp, objv[2], &normalize) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGlyphsNormalizeScale(name, normalize);
+    } else {
+        g_renderer->setGlyphsNormalizeScale("all", normalize);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Glyphs>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Glyphs>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Glyphs>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Glyphs>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsOrientGlyphsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGlyphsOrientMode(name, state, fieldName);
+    } else {
+        g_renderer->setGlyphsOrientMode("all", state, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsPointSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    float size;
+    if (GetFloatFromObj(interp, objv[2], &size) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectPointSize<Glyphs>(name, size);
+    } else {
+        g_renderer->setGraphicsObjectPointSize<Glyphs>("all", size);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Glyphs>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Glyphs>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsQualityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double quality;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quality) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGlyphsQuality(name, quality);
+    } else {
+        g_renderer->setGlyphsQuality("all", quality);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Glyphs>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Glyphs>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsScaleFactorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double scale;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGlyphsScaleFactor(name, scale);
+    } else {
+        g_renderer->setGlyphsScaleFactor("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsScalingModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    Glyphs::ScalingMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Glyphs::SCALE_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Glyphs::SCALE_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vcomp") == 0) {
+        mode = Glyphs::SCALE_BY_VECTOR_COMPONENTS;
+    } else if (str[0] == 'o' && strcmp(str, "off") == 0) {
+        mode = Glyphs::SCALING_OFF;
+    } else {
+        Tcl_AppendResult(interp, "bad scaling mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vcomp', 'off'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGlyphsScalingMode(name, mode, fieldName);
+    } else {
+        g_renderer->setGlyphsScalingMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsShapeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    Glyphs::GlyphShape shape;
+
+    const char *shapeOpt = Tcl_GetString(objv[2]);
+    if (shapeOpt[0] == 'a' && strcmp(shapeOpt, "arrow") == 0) {
+        shape = Glyphs::ARROW;
+    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cone") == 0) {
+        shape = Glyphs::CONE;
+    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cube") == 0) {
+        shape = Glyphs::CUBE;
+    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cylinder") == 0) {
+        shape = Glyphs::CYLINDER;
+    } else if (shapeOpt[0] == 'd' && strcmp(shapeOpt, "dodecahedron") == 0) {
+        shape = Glyphs::DODECAHEDRON;
+    } else if (shapeOpt[0] == 'i' && strcmp(shapeOpt, "icosahedron") == 0) {
+        shape = Glyphs::ICOSAHEDRON;
+    } else if (shapeOpt[0] == 'l' && strcmp(shapeOpt, "line") == 0) {
+        shape = Glyphs::LINE;
+    } else if (shapeOpt[0] == 'o' && strcmp(shapeOpt, "octahedron") == 0) {
+        shape = Glyphs::OCTAHEDRON;
+    } else if (shapeOpt[0] == 'p' && strcmp(shapeOpt, "point") == 0) {
+        shape = Glyphs::POINT;
+    } else if (shapeOpt[0] == 's' && strcmp(shapeOpt, "sphere") == 0) {
+        shape = Glyphs::SPHERE;
+    } else if (shapeOpt[0] == 't' && strcmp(shapeOpt, "tetrahedron") == 0) {
+        shape = Glyphs::TETRAHEDRON;
+    } else {
+        Tcl_AppendResult(interp, "bad shape option \"", shapeOpt,
+                         "\": should be one of: 'arrow', 'cone', 'cube', 'cylinder', 'dodecahedron', 'icosahedron', 'line', 'octahedron', 'point', 'sphere', 'tetrahedron'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGlyphsShape(name, shape);
+    } else {
+        g_renderer->setGlyphsShape("all", shape);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Glyphs>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Glyphs>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+GlyphsWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Glyphs>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Glyphs>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec glyphsOps[] = {
+    {"add",          1, GlyphsAddOp, 3, 4, "shape ?dataSetName?"},
+    {"ccolor",       2, GlyphsColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"color",        5, GlyphsColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, GlyphsColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, GlyphsColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"delete",       1, GlyphsDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, GlyphsEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"gorient",      2, GlyphsOrientGlyphsOp, 4, 5, "bool fieldName ?dataSetName?"},
+    {"gscale",       2, GlyphsScaleFactorOp, 3, 4, "scaleFactor ?dataSetName?"},
+    {"lighting",     3, GlyphsLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, GlyphsLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, GlyphsLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"normscale",    2, GlyphsNormalizeScaleOp, 3, 4, "bool ?dataSetName?"},
+    {"numglyphs",    2, GlyphsMaxNumberOfGlyphsOp, 3, 7, "max ?random? ?offset? ?ratio? ?dataSetName?"},
+    {"opacity",      2, GlyphsOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, GlyphsOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          2, GlyphsPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"ptsize",       2, GlyphsPointSizeOp, 3, 4, "size ?dataSetName?"},
+    {"quality",      1, GlyphsQualityOp, 3, 4, "val ?dataSetName?"},
+    {"scale",        2, GlyphsScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"shape",        2, GlyphsShapeOp, 3, 4, "shapeVal ?dataSetName?"},
+    {"smode",        2, GlyphsScalingModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"visible",      1, GlyphsVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"wireframe",    1, GlyphsWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nGlyphsOps = NumCmdSpecs(glyphsOps);
+
+static int
+GlyphsCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nGlyphsOps, glyphsOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+GroupAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    int numNodes;
+    Tcl_Obj **nodes = NULL;
+    if (Tcl_ListObjGetElements(interp, objv[2], &numNodes, &nodes) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    std::vector<Group::NodeId> nodeList;
+    for (int i = 0; i < numNodes; i++) {
+        nodeList.push_back(Tcl_GetString(nodes[i]));
+    }
+    const char *name = Tcl_GetString(objv[3]);
+    if (!g_renderer->addGroup(name, nodeList)) {
+        Tcl_AppendResult(interp, "Failed to create group", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+GroupDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Group>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Group>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+GroupOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Group>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Group>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+GroupOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Group>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Group>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+GroupPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Group>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Group>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+GroupRemoveChildOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    int numNodes;
+    Tcl_Obj **nodes = NULL;
+    if (Tcl_ListObjGetElements(interp, objv[2], &numNodes, &nodes) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    std::vector<Group::NodeId> nodeList;
+    for (int i = 0; i < numNodes; i++) {
+        nodeList.push_back(Tcl_GetString(nodes[i]));
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->removeChildrenFromGroup(name, nodeList);
+    } else {
+        g_renderer->removeChildrenFromGroup("all", nodeList);
+    }
+    return TCL_OK;
+}
+
+static int
+GroupScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Group>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Group>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+GroupVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Group>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Group>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec groupOps[] = {
+    {"add",       1, GroupAddOp, 4, 4, "nodeList groupName"},
+    {"delete",    1, GroupDeleteOp, 2, 3, "?name?"},
+    {"orient",    4, GroupOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, GroupOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, GroupPositionOp, 5, 6, "x y z ?name?"},
+    {"remove",    1, GroupRemoveChildOp, 3, 4, "nodeList ?name?"},
+    {"scale",     1, GroupScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"visible",   1, GroupVisibleOp, 3, 4, "bool ?name?"},
+};
+static int nGroupOps = NumCmdSpecs(groupOps);
+
+static int
+GroupCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+         Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nGroupOps, groupOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+HeightMapAddContourListOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    std::vector<double> contourList;
+    double heightScale;
+
+    int clistc;
+    Tcl_Obj **clistv;
+
+    if (Tcl_ListObjGetElements(interp, objv[3], &clistc, &clistv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    for (int i = 0; i < clistc; i++) {
+        double val;
+        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        contourList.push_back(val);
+    }
+
+    if (Tcl_GetDoubleFromObj(interp, objv[4], &heightScale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        if (!g_renderer->addHeightMap(name, contourList, heightScale)) {
+            Tcl_AppendResult(interp, "Failed to create heightmap", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addHeightMap("all", contourList, heightScale)) {
+            Tcl_AppendResult(interp, "Failed to create heightmap for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapAddNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    int numContours;
+    double heightScale;
+    if (Tcl_GetIntFromObj(interp, objv[3], &numContours) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[4], &heightScale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        if (!g_renderer->addHeightMap(name, numContours, heightScale)) {
+            Tcl_AppendResult(interp, "Failed to create heightmap", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addHeightMap("all", numContours, heightScale)) {
+            Tcl_AppendResult(interp, "Failed to create heightmap for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static CmdSpec heightmapAddOps[] = {
+    {"contourlist", 1, HeightMapAddContourListOp, 5, 6, "contourList heightscale ?dataSetName?"},
+    {"numcontours", 1, HeightMapAddNumContoursOp, 5, 6, "numContours heightscale ?dataSetName?"}
+};
+static int nHeightmapAddOps = NumCmdSpecs(heightmapAddOps);
+
+static int
+HeightMapAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nHeightmapAddOps, heightmapAddOps,
+                        CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+HeightMapAspectOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double aspect;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &aspect) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectAspect<HeightMap>(name, aspect);
+    } else {
+        g_renderer->setGraphicsObjectAspect<HeightMap>("all", aspect);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapCloudStyleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    HeightMap::CloudStyle style;
+    char *str =  Tcl_GetString(objv[2]);
+    if (str[0] == 'm' && strcmp(str, "mesh") == 0) {
+        style = HeightMap::CLOUD_MESH;
+    } else if (str[0] == 's' && strcmp(str, "splat") == 0) {
+        style = HeightMap::CLOUD_SPLAT;
+    } else {
+        Tcl_AppendResult(interp, "bad cloudstyle option \"", str,
+                         "\": should be one of: 'mesh', 'splat'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapCloudStyle(name, style);
+    } else {
+        g_renderer->setHeightMapCloudStyle("all", style);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<HeightMap>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<HeightMap>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<HeightMap>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<HeightMap>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    HeightMap::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = HeightMap::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = HeightMap::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == HeightMap::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setHeightMapColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setHeightMapColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapColorMode2Op(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    HeightMap::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = HeightMap::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = HeightMap::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = HeightMap::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == HeightMap::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    str = Tcl_GetString(objv[4]);
+    DataSet::DataAttributeType association = DataSet::POINT_DATA;
+    bool haveAssoc = false;
+    if (str[0] == 'p' && strcmp(str, "point_data") == 0) {
+        association = DataSet::POINT_DATA;
+        haveAssoc = true;
+    } else if (str[0] == 'c' && strcmp(str, "cell_data") == 0) {
+        association = DataSet::CELL_DATA;
+        haveAssoc = true;
+    } else if (str[0] == 'f' && strcmp(str, "field_data") == 0) {
+        association = DataSet::FIELD_DATA;
+        haveAssoc = true;
+    } else if (str[0] == 'a' && strcmp(str, "any") == 0) {
+        haveAssoc = false;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode association option \"", str,
+                         "\": should be one of: 'point_data', 'cell_data', 'field_data', 'any'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    double *range = NULL;
+    if (objc >= 7) {
+        range = new double[2];
+        if (Tcl_GetDoubleFromObj(interp, objv[5], &range[0]) != TCL_OK ||
+            Tcl_GetDoubleFromObj(interp, objv[6], &range[1]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    if (objc == 6 || objc == 8) {
+        const char *name = Tcl_GetString((objc == 6 ? objv[5] : objv[7]));
+        if (haveAssoc) {
+            g_renderer->setHeightMapColorMode(name, mode, association, fieldName, range);
+        } else {
+            g_renderer->setHeightMapColorMode(name, mode, fieldName, range);
+        }
+    } else {
+        if (haveAssoc) {
+            g_renderer->setHeightMapColorMode("all", mode, association, fieldName, range);
+        } else {
+            g_renderer->setHeightMapColorMode("all", mode, fieldName, range);
+        }
+    }
+    if (range != NULL) {
+        delete [] range;
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapContourLineColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapContourLineColorMapEnabled(name, state);
+    } else {
+        g_renderer->setHeightMapContourLineColorMapEnabled("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapContourLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                            Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setHeightMapContourEdgeColor(name, color);
+    } else {
+        g_renderer->setHeightMapContourEdgeColor("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapContourLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                            Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapContourEdgeWidth(name, width);
+    } else {
+        g_renderer->setHeightMapContourEdgeWidth("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapContourListOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    std::vector<double> contourList;
+
+    int clistc;
+    Tcl_Obj **clistv;
+
+    if (Tcl_ListObjGetElements(interp, objv[2], &clistc, &clistv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    for (int i = 0; i < clistc; i++) {
+        double val;
+        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        contourList.push_back(val);
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapContourList(name, contourList);
+    } else {
+        g_renderer->setHeightMapContourList("all", contourList);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapContourLineVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapContourLineVisibility(name, state);
+    } else {
+        g_renderer->setHeightMapContourLineVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapContourSurfaceVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapContourSurfaceVisibility(name, state);
+    } else {
+        g_renderer->setHeightMapContourSurfaceVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<HeightMap>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<HeightMap>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<HeightMap>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<HeightMap>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapHeightScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double scale;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapHeightScale(name, scale);
+    } else {
+        g_renderer->setHeightMapHeightScale("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<HeightMap>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<HeightMap>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<HeightMap>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<HeightMap>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<HeightMap>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<HeightMap>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    int numContours;
+
+    if (Tcl_GetIntFromObj(interp, objv[2], &numContours) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setHeightMapNumContours(name, numContours);
+    } else {
+        g_renderer->setHeightMapNumContours("all", numContours);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<HeightMap>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<HeightMap>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<HeightMap>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<HeightMap>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<HeightMap>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<HeightMap>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapPreInterpOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<HeightMap>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<HeightMap>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<HeightMap>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<HeightMap>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<HeightMap>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<HeightMap>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapVolumeSliceOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double ratio;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Axis axis;
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        axis = X_AXIS;
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        axis = Y_AXIS;
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        axis = Z_AXIS;
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName ratio", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGraphicsObjectVolumeSlice<HeightMap>(name, axis, ratio);
+    } else {
+        g_renderer->setGraphicsObjectVolumeSlice<HeightMap>("all", axis, ratio);
+    }
+    return TCL_OK;
+}
+
+static int
+HeightMapWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<HeightMap>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<HeightMap>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec heightmapOps[] = {
+    {"add",          2, HeightMapAddOp, 5, 6, "oper value heightscale ?dataSetName?"},
+    {"aspect",       2, HeightMapAspectOp, 3, 4, "aspect ?dataSetName?"},
+    {"ccolor",       2, HeightMapColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"cloudstyle",   2, HeightMapCloudStyleOp, 3, 4, "style ?dataSetName?"},
+    {"color",        5, HeightMapColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, HeightMapColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    9, HeightMapColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"colormode2",   10,HeightMapColorMode2Op, 5, 8, "mode fieldName association ?min max? ?dataSetName?"},
+    {"contourlist",  3, HeightMapContourListOp, 3, 4, "contourList ?dataSetName?"},
+    {"delete",       1, HeightMapDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, HeightMapEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"heightscale",  1, HeightMapHeightScaleOp, 3, 4, "value ?dataSetName?"},
+    {"isolinecmap",  9, HeightMapContourLineColorMapOp, 3, 4, "bool ?dataSetName?"},
+    {"isolinecolor", 9, HeightMapContourLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"isolines",     8, HeightMapContourLineVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"isolinewidth", 8, HeightMapContourLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"lighting",     3, HeightMapLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, HeightMapLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, HeightMapLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"numcontours",  1, HeightMapNumContoursOp, 3, 4, "numContours ?dataSetName?"},
+    {"opacity",      2, HeightMapOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, HeightMapOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          2, HeightMapPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"preinterp",    2, HeightMapPreInterpOp, 3, 4, "bool ?dataSetName?"},
+    {"scale",        2, HeightMapScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"slice",        2, HeightMapVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"},
+    {"surface",      2, HeightMapContourSurfaceVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"visible",      1, HeightMapVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"wireframe",    1, HeightMapWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nHeightmapOps = NumCmdSpecs(heightmapOps);
+
+static int
+HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nHeightmapOps, heightmapOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ImageAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<Image>(name)) {
+            Tcl_AppendResult(interp, "Failed to create image", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<Image>("all")) {
+            Tcl_AppendResult(interp, "Failed to create iamge for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+ImageBackgroundOp(ClientData clientData, Tcl_Interp *interp, int objc,
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageBackground(name, state);
+    } else {
+        g_renderer->setImageBackground("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageBackingOp(ClientData clientData, Tcl_Interp *interp, int objc,
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageBacking(name, state);
+    } else {
+        g_renderer->setImageBacking("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageBorderOp(ClientData clientData, Tcl_Interp *interp, int objc,
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageBorder(name, state);
+    } else {
+        g_renderer->setImageBorder("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Image>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Image>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Image>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Image>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Image>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Image>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+ImageExtentsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    int extents[6];
+    if (Tcl_GetIntFromObj(interp, objv[2], &extents[0]) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &extents[1]) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[4], &extents[2]) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[5], &extents[3]) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[6], &extents[4]) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[7], &extents[5]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 9) {
+        const char *name = Tcl_GetString(objv[8]);
+        g_renderer->setImageExtents(name, extents);
+    } else {
+        g_renderer->setImageExtents("all", extents);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageFollowCameraOp(ClientData clientData, Tcl_Interp *interp, int objc,
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageSliceFollowsCamera(name, state);
+    } else {
+        g_renderer->setImageSliceFollowsCamera("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageLevelOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double level;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &level) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageLevel(name, level);
+    } else {
+        g_renderer->setImageLevel("all", level);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Image>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Image>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Image>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Image>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+ImagePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Image>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Image>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Image>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Image>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageSliceInterpOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageSliceInterp(name, state);
+    } else {
+        g_renderer->setImageSliceInterp("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageSlicePlaneOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double normal[3], origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &normal[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &normal[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &normal[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[6], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[7], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[8]);
+        g_renderer->setImageSlicePlane(name, normal, origin);
+    } else {
+        g_renderer->setImageSlicePlane("all", normal, origin);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Image>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Image>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageWindowOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double window;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &window) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageWindow(name, window);
+    } else {
+        g_renderer->setImageWindow("all", window);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageZSliceOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    int slice;
+    if (Tcl_GetIntFromObj(interp, objv[2], &slice) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageZSlice(name, slice);
+    } else {
+        g_renderer->setImageZSlice("all", slice);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec imageOps[] = {
+    {"add",          1, ImageAddOp, 2, 3, "?dataSetName?"},
+    {"backing",      2, ImageBackingOp, 3, 4, "bool ?dataSetName?"},
+    {"bg",           2, ImageBackgroundOp, 3, 4, "bool ?dataSetName?"},
+    {"border",       2, ImageBorderOp, 3, 4, "bool ?dataSetName?"},
+    {"color",        5, ImageColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     6, ImageColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"delete",       1, ImageDeleteOp, 2, 3, "?dataSetName?"},
+    {"extents",      1, ImageExtentsOp, 8, 9, "xmin xmax ymin ymax zmin zmax ?dataSetName?"},
+    {"follow",       1, ImageFollowCameraOp, 3, 4, "bool ?dataSetName?"},
+    {"level",        1, ImageLevelOp, 3, 4, "val ?dataSetName?"},
+    {"opacity",      2, ImageOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, ImageOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          1, ImagePositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"scale",        2, ImageScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"sinterp",      2, ImageSliceInterpOp, 3, 4, "bool ?dataSetName?"},
+    {"slice",        2, ImageSlicePlaneOp, 8, 9, "nx ny nz ox oy oz ?dataSetName?"},
+    {"visible",      1, ImageVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"window",       1, ImageWindowOp, 3, 4, "val ?dataSetName?"},
+    {"zslice",       1, ImageZSliceOp, 3, 4, "val ?dataSetName?"}
+};
+static int nImageOps = NumCmdSpecs(imageOps);
+
+static int
+ImageCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+         Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nImageOps, imageOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ImageCutplaneAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<ImageCutplane>(name)) {
+            Tcl_AppendResult(interp, "Failed to create imgcutplane", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<ImageCutplane>("all")) {
+            Tcl_AppendResult(interp, "Failed to create imgcutplane for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<ImageCutplane>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<ImageCutplane>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<ImageCutplane>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<ImageCutplane>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<ImageCutplane>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<ImageCutplane>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneLevelOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    double level;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &level) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageCutplaneLevel(name, level);
+    } else {
+        g_renderer->setImageCutplaneLevel("all", level);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    double ambient, diffuse;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGraphicsObjectAmbient<ImageCutplane>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<ImageCutplane>(name, diffuse);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<ImageCutplane>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<ImageCutplane>("all", diffuse);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<ImageCutplane>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<ImageCutplane>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<ImageCutplane>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<ImageCutplane>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageCutplaneOutlineVisibility(name, state);
+    } else {
+        g_renderer->setImageCutplaneOutlineVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplanePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<ImageCutplane>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<ImageCutplane>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<ImageCutplane>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<ImageCutplane>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneSliceVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Axis axis;
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        axis = X_AXIS;
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        axis = Y_AXIS;
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        axis = Z_AXIS;
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName bool", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setImageCutplaneSliceVisibility(name, axis, state);
+    } else {
+        g_renderer->setImageCutplaneSliceVisibility("all", axis, state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<ImageCutplane>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<ImageCutplane>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneVolumeSliceOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                           Tcl_Obj *const *objv)
+{
+    double ratio;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Axis axis;
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        axis = X_AXIS;
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        axis = Y_AXIS;
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        axis = Z_AXIS;
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName ratio", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGraphicsObjectVolumeSlice<ImageCutplane>(name, axis, ratio);
+    } else {
+        g_renderer->setGraphicsObjectVolumeSlice<ImageCutplane>("all", axis, ratio);
+    }
+    return TCL_OK;
+}
+
+static int
+ImageCutplaneWindowOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double window;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &window) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setImageCutplaneWindow(name, window);
+    } else {
+        g_renderer->setImageCutplaneWindow("all", window);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec imageCutplaneOps[] = {
+    {"add",          2, ImageCutplaneAddOp, 2, 3, "?dataSetName?"},
+    {"axis",         2, ImageCutplaneSliceVisibilityOp, 4, 5, "axis bool ?dataSetName?"},
+    {"color",        5, ImageCutplaneColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     6, ImageCutplaneColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"delete",       1, ImageCutplaneDeleteOp, 2, 3, "?dataSetName?"},
+    {"level",        1, ImageCutplaneLevelOp, 3, 4, "level ?dataSetName?"},
+    {"material",     1, ImageCutplaneMaterialOp, 4, 5, "ambientCoeff diffuseCoeff ?dataSetName?"},
+    {"opacity",      2, ImageCutplaneOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, ImageCutplaneOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"outline",      2, ImageCutplaneOutlineOp, 3, 4, "bool ?dataSetName?"},
+    {"pos",          2, ImageCutplanePositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"scale",        2, ImageCutplaneScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"slice",        2, ImageCutplaneVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"},
+    {"visible",      1, ImageCutplaneVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"window",       1, ImageCutplaneWindowOp, 3, 4, "window ?dataSetName?"}
+};
+static int nImageCutplaneOps = NumCmdSpecs(imageCutplaneOps);
+
+static int
+ImageCutplaneCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nImageCutplaneOps, imageCutplaneOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ImageFlushCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    lastCmdStatus = TCL_BREAK;
+    return TCL_OK;
+}
+
+static int
+LegendCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    if (objc < 8) {
+        Tcl_AppendResult(interp, "wrong # args: should be \"",
+                Tcl_GetString(objv[0]), " colormapName legendType fieldName title width height numLabels ?dataSetName?\"", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *colorMapName = Tcl_GetString(objv[1]);
+    const char *typeStr = Tcl_GetString(objv[2]);
+    Renderer::LegendType legendType;
+    if (typeStr[0] == 's' && strcmp(typeStr, "scalar") == 0) {
+        legendType = Renderer::LEGEND_SCALAR;
+    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vmag") == 0) {
+        legendType = Renderer::LEGEND_VECTOR_MAGNITUDE;
+    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vx") == 0) {
+        legendType = Renderer::LEGEND_VECTOR_X;
+    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vy") == 0) {
+        legendType = Renderer::LEGEND_VECTOR_Y;
+    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vz") == 0) {
+        legendType = Renderer::LEGEND_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "Bad legendType option \"",
+                         typeStr, "\", should be one of 'scalar', 'vmag', 'vx', 'vy', 'vz'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    const char *fieldName = Tcl_GetString(objv[3]);
+
+    std::string title(Tcl_GetString(objv[4]));
+    int width, height, numLabels;
+
+    if (Tcl_GetIntFromObj(interp, objv[5], &width) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[6], &height) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[7], &numLabels) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    vtkSmartPointer<vtkUnsignedCharArray> imgData = 
+        vtkSmartPointer<vtkUnsignedCharArray>::New();
+
+    double range[2];
+    range[0] = DBL_MAX;
+    range[1] = -DBL_MAX;
+
+    bool opaque = true;
+
+    if (objc == 9) {
+        const char *dataSetName = Tcl_GetString(objv[8]);
+        if (!g_renderer->renderColorMap(colorMapName, dataSetName, legendType, fieldName, title,
+                                        range, width, height, opaque, numLabels, imgData)) {
+            Tcl_AppendResult(interp, "Color map \"",
+                             colorMapName, "\" or dataset \"",
+                             dataSetName, "\" was not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->renderColorMap(colorMapName, "all", legendType, fieldName, title,
+                                        range, width, height, opaque, numLabels, imgData)) {
+            Tcl_AppendResult(interp, "Color map \"",
+                             colorMapName, "\" was not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+
+#ifdef DEBUG
+# ifdef RENDER_TARGA
+    writeTGAFile("/tmp/legend.tga", imgData->GetPointer(0), width, height,
+                 TARGA_BYTES_PER_PIXEL);
+# else
+    writeTGAFile("/tmp/legend.tga", imgData->GetPointer(0), width, height,
+                 TARGA_BYTES_PER_PIXEL, true);
+# endif
+#else
+    char cmd[256];
+    snprintf(cmd, sizeof(cmd), "nv>legend {%s} {%s} %g %g",
+             colorMapName, title.c_str(), range[0], range[1]);
+
+# ifdef USE_THREADS
+#  ifdef RENDER_TARGA
+    ResponseQueue *queue = (ResponseQueue *)clientData;
+    queueTGA(queue, cmd, imgData->GetPointer(0), width, height,
+             TARGA_BYTES_PER_PIXEL);
+#  else
+    ResponseQueue *queue = (ResponseQueue *)clientData;
+    queuePPM(queue, cmd, imgData->GetPointer(0), width, height);
+#  endif
+# else
+#  ifdef RENDER_TARGA
+    writeTGA(g_fdOut, cmd, imgData->GetPointer(0), width, height,
+             TARGA_BYTES_PER_PIXEL);
+#  else
+    writePPM(g_fdOut, cmd, imgData->GetPointer(0), width, height);
+#  endif
+# endif // USE_THREADS
+#endif // DEBUG
+    return TCL_OK;
+}
+
+static int
+LegendSimpleCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    if (objc != 4) {
+        Tcl_AppendResult(interp, "wrong # args: should be \"",
+                Tcl_GetString(objv[0]), " colormapName width height\"", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *colorMapName = Tcl_GetString(objv[1]);
+
+    int width, height;
+    if (Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    vtkSmartPointer<vtkUnsignedCharArray> imgData = 
+        vtkSmartPointer<vtkUnsignedCharArray>::New();
+
+    bool opaque = true;
+
+    if (!g_renderer->renderColorMap(colorMapName, width, height, opaque, imgData)) {
+        Tcl_AppendResult(interp, "Color map \"",
+                         colorMapName, "\" was not found", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+#ifdef DEBUG
+# ifdef RENDER_TARGA
+    writeTGAFile("/tmp/legend.tga", imgData->GetPointer(0), width, height,
+                 TARGA_BYTES_PER_PIXEL);
+# else
+    writeTGAFile("/tmp/legend.tga", imgData->GetPointer(0), width, height,
+                 TARGA_BYTES_PER_PIXEL, true);
+# endif
+#else
+    char cmd[256];
+    snprintf(cmd, sizeof(cmd), "nv>legend {%s} {} 0 1", colorMapName);
+
+# ifdef USE_THREADS
+#  ifdef RENDER_TARGA
+    ResponseQueue *queue = (ResponseQueue *)clientData;
+    queueTGA(queue, cmd, imgData->GetPointer(0), width, height,
+             TARGA_BYTES_PER_PIXEL);
+#  else
+    ResponseQueue *queue = (ResponseQueue *)clientData;
+    queuePPM(queue, cmd, imgData->GetPointer(0), width, height);
+#  endif
+# else
+#  ifdef RENDER_TARGA
+    writeTGA(g_fdOut, cmd, imgData->GetPointer(0), width, height,
+             TARGA_BYTES_PER_PIXEL);
+#  else
+    writePPM(g_fdOut, cmd, imgData->GetPointer(0), width, height);
+#  endif
+# endif // USE_THREADS
+#endif // DEBUG
+
+    return TCL_OK;
+}
+
+static int
+LICAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+         Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<LIC>(name)) {
+            Tcl_AppendResult(interp, "Failed to create lic", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<LIC>("all")) {
+            Tcl_AppendResult(interp, "Failed to create lic for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+LICColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<LIC>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<LIC>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+LICDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<LIC>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<LIC>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+LICEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<LIC>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<LIC>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+LICLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<LIC>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<LIC>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+LICLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<LIC>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<LIC>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+LICLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<LIC>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<LIC>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+LICOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<LIC>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<LIC>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+LICOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<LIC>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<LIC>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+LICPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<LIC>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<LIC>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+LICPreInterpOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<LIC>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<LIC>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+LICScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<LIC>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<LIC>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+LICVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<LIC>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<LIC>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+LICVolumeSliceOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double ratio;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Axis axis;
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        axis = X_AXIS;
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        axis = Y_AXIS;
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        axis = Z_AXIS;
+    } else {
+        Tcl_AppendResult(interp, "bad axis option \"", string,
+                         "\": should be axisName ratio", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGraphicsObjectVolumeSlice<LIC>(name, axis, ratio);
+    } else {
+        g_renderer->setGraphicsObjectVolumeSlice<LIC>("all", axis, ratio);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec licOps[] = {
+    {"add",          1, LICAddOp, 2, 3, "?dataSetName?"},
+    {"colormap",     1, LICColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"delete",       1, LICDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, LICEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"lighting",     3, LICLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, LICLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, LICLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"opacity",      2, LICOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, LICOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          2, LICPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"preinterp",    2, LICPreInterpOp, 3, 4, "bool ?dataSetName?"},
+    {"scale",        2, LICScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"slice",        2, LICVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"},
+    {"visible",      1, LICVisibleOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nLICOps = NumCmdSpecs(licOps);
+
+static int
+LICCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nLICOps, licOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+LineAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    std::vector<double> points;
+    int ptlistc;
+    Tcl_Obj **ptlistv;
+    if (Tcl_ListObjGetElements(interp, objv[2], &ptlistc, &ptlistv) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (ptlistc < 6 || ptlistc % 3 != 0) {
+        Tcl_AppendResult(interp, "Points list size must be 6 or more and a multiple of 3", (char*)NULL);
+        return TCL_ERROR;
+    }
+    for (int i = 0; i < ptlistc; i++) {
+        double val;
+        if (Tcl_GetDoubleFromObj(interp, ptlistv[i], &val) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        points.push_back(val);
+    }
+    const char *name = Tcl_GetString(objv[3]);
+    if (!g_renderer->addLine(name, points)) {
+        Tcl_AppendResult(interp, "Failed to create line", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+LineDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Line>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Line>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+LineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Line>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Line>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+LineLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Line>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Line>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+LineOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Line>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Line>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+LineOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Line>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Line>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+LineOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Line>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Line>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+LinePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Line>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Line>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+LineScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Line>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Line>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+LineVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Line>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Line>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec lineOps[] = {
+    {"add",       1, LineAddOp, 4, 4, "points name"},
+    {"color",     1, LineColorOp, 5, 6, "r g b ?name?"},
+    {"delete",    1, LineDeleteOp, 2, 3, "?name?"},
+    {"linecolor", 5, LineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, LineLineWidthOp, 3, 4, "width ?name?"},
+    {"opacity",   2, LineOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, LineOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, LineOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, LinePositionOp, 5, 6, "x y z ?name?"},
+    {"scale",     1, LineScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"visible",   1, LineVisibleOp, 3, 4, "bool ?name?"}
+};
+static int nLineOps = NumCmdSpecs(lineOps);
+
+static int
+LineCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nLineOps, lineOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+MoleculeAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<Molecule>(name)) {
+            Tcl_AppendResult(interp, "Failed to create molecule", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<Molecule>("all")) {
+            Tcl_AppendResult(interp, "Failed to create molecule for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeAtomVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeAtomVisibility(name, state);
+    } else {
+        g_renderer->setMoleculeAtomVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeAtomLabelFieldOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    const char *fieldName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeAtomLabelField(name, fieldName);
+    } else {
+        g_renderer->setMoleculeAtomLabelField("all", fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeAtomLabelVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeAtomLabelVisibility(name, state);
+    } else {
+        g_renderer->setMoleculeAtomLabelVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeAtomQualityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double quality;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quality) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeAtomQuality(name, quality);
+    } else {
+        g_renderer->setMoleculeAtomQuality("all", quality);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeAtomScaleFactorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    double scale;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeAtomRadiusScale(name, scale);
+    } else {
+        g_renderer->setMoleculeAtomRadiusScale("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeAtomScalingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    Molecule::AtomScaling scaling;
+    const char *scalingOpt = Tcl_GetString(objv[2]);
+    if (scalingOpt[0] == 'v' && strcmp(scalingOpt, "van_der_waals") == 0) {
+        scaling = Molecule::VAN_DER_WAALS_RADIUS;
+    } else if (scalingOpt[0] == 'c' && strcmp(scalingOpt, "covalent") == 0) {
+        scaling = Molecule::COVALENT_RADIUS;
+    } else if (scalingOpt[0] == 'a' && strcmp(scalingOpt, "atomic") == 0) {
+        scaling = Molecule::ATOMIC_RADIUS;
+    } else if (scalingOpt[0] == 'n' && strcmp(scalingOpt, "none") == 0) {
+        scaling = Molecule::NO_ATOM_SCALING;
+    } else {
+        Tcl_AppendResult(interp, "bad atomscale option \"", scalingOpt,
+                         "\": should be van_der_waals, covalent, atomic, or none", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeAtomScaling(name, scaling);
+    } else {
+        g_renderer->setMoleculeAtomScaling("all", scaling);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeBondColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    Molecule::BondColorMode mode;
+    const char *colorMode = Tcl_GetString(objv[2]);
+    if (colorMode[0] == 'c' && strcmp(colorMode, "constant") == 0) {
+        mode = Molecule::BOND_COLOR_CONSTANT;
+    } else if (colorMode[0] == 'b' && strcmp(colorMode, "by_elements") == 0) {
+        mode = Molecule::BOND_COLOR_BY_ELEMENTS;
+    } else {
+        Tcl_AppendResult(interp, "bad bcmode option \"", colorMode,
+                         "\": should be constant or by_elements", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeBondColorMode(name, mode);
+    } else {
+        g_renderer->setMoleculeBondColorMode("all", mode);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeBondColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setMoleculeBondColor(name, color);
+    } else {
+        g_renderer->setMoleculeBondColor("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeBondQualityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double quality;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quality) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeBondQuality(name, quality);
+    } else {
+        g_renderer->setMoleculeBondQuality("all", quality);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeBondScaleFactorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    double scale;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeBondRadiusScale(name, scale);
+    } else {
+        g_renderer->setMoleculeBondRadiusScale("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeBondStyleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    Molecule::BondStyle style;
+    const char *styleStr = Tcl_GetString(objv[2]);
+    if (styleStr[0] == 'c' && strcmp(styleStr, "cylinder") == 0) {
+        style = Molecule::BOND_STYLE_CYLINDER;
+    } else if (styleStr[0] == 'l' && strcmp(styleStr, "line") == 0) {
+        style = Molecule::BOND_STYLE_LINE;
+    } else {
+        Tcl_AppendResult(interp, "bad bstyle option \"", styleStr,
+                         "\": should be cylinder or line", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeBondStyle(name, style);
+    } else {
+        g_renderer->setMoleculeBondStyle("all", style);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeBondVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setMoleculeBondVisibility(name, state);
+    } else {
+        g_renderer->setMoleculeBondVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Molecule>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Molecule>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Molecule>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Molecule>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    Molecule::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'b' && strcmp(str, "by_elements") == 0) {
+        mode = Molecule::COLOR_BY_ELEMENTS;
+    } else if (str[0] == 'c' &&
+               (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = Molecule::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Molecule::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Molecule::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = Molecule::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = Molecule::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = Molecule::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'by_elements', 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == Molecule::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+#if 0
+    else if (mode == Molecule::COLOR_BY_ELEMENTS) {
+        fieldName = "element";
+    }
+#endif
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setMoleculeColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setMoleculeColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Molecule>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Molecule>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Molecule>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Molecule>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Molecule>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Molecule>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Molecule>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Molecule>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Molecule>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Molecule>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Molecule>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Molecule>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Molecule>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Molecule>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Molecule>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Molecule>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Molecule>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Molecule>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Molecule>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Molecule>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Molecule>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Molecule>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Molecule>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Molecule>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Molecule>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Molecule>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+MoleculeWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Molecule>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Molecule>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec moleculeOps[] = {
+    {"add",          2, MoleculeAddOp, 2, 3, "?dataSetName?"},
+    {"aquality",     2, MoleculeAtomQualityOp, 3, 4, "value ?dataSetName?"},
+    {"ascale",       2, MoleculeAtomScaleFactorOp, 3, 4, "value ?dataSetName?"},
+    {"atoms",        2, MoleculeAtomVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"bcmode",       3, MoleculeBondColorModeOp, 3, 4, "mode ?dataSetName?"},
+    {"bcolor",       3, MoleculeBondColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"bonds",        2, MoleculeBondVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"bquality",     2, MoleculeBondQualityOp, 3, 4, "value ?dataSetName?"},
+    {"bscale",       3, MoleculeBondScaleFactorOp, 3, 4, "value ?dataSetName?"},
+    {"bstyle",       3, MoleculeBondStyleOp, 3, 4, "value ?dataSetName?"},
+    {"ccolor",       2, MoleculeColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"color",        5, MoleculeColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, MoleculeColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, MoleculeColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"delete",       1, MoleculeDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, MoleculeEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"labels",       2, MoleculeAtomLabelVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"lfield",       2, MoleculeAtomLabelFieldOp, 3, 4, "fieldName ?dataSetName?"},
+    {"lighting",     3, MoleculeLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, MoleculeLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, MoleculeLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"material",     1, MoleculeMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",      2, MoleculeOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, MoleculeOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          1, MoleculePositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"rscale",       1, MoleculeAtomScalingOp, 3, 4, "scaling ?dataSetName?"},
+    {"scale",        1, MoleculeScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"visible",      1, MoleculeVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"wireframe",    1, MoleculeWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nMoleculeOps = NumCmdSpecs(moleculeOps);
+
+static int
+MoleculeCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nMoleculeOps, moleculeOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+OutlineAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<Outline>(name)) {
+            Tcl_AppendResult(interp, "Failed to create Outline", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<Outline>("all")) {
+            Tcl_AppendResult(interp, "Failed to create Outline for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+OutlineDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Outline>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Outline>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+OutlineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Outline>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Outline>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+OutlineLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Outline>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Outline>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+OutlineOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Outline>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Outline>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+OutlineOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Outline>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Outline>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+OutlinePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Outline>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Outline>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+OutlineScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Outline>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Outline>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+OutlineVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Outline>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Outline>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec outlineOps[] = {
+    {"add",       1, OutlineAddOp, 2, 3, "?dataSetName?"},
+    {"color",     1, OutlineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"delete",    1, OutlineDeleteOp, 2, 3, "?dataSetName?"},
+    {"linecolor", 5, OutlineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth", 5, OutlineLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"opacity",   2, OutlineOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",    2, OutlineOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",       1, OutlinePositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"scale",     1, OutlineScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"visible",   1, OutlineVisibleOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nOutlineOps = NumCmdSpecs(outlineOps);
+
+static int
+OutlineCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nOutlineOps, outlineOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ParallelepipedAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double vec1[3], vec2[3], vec3[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &vec1[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &vec1[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &vec1[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &vec2[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[6], &vec2[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[7], &vec2[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[8], &vec3[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[9], &vec3[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[10], &vec3[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[11]);
+    if (!g_renderer->addParallelepiped(name, vec1, vec2, vec3)) {
+        Tcl_AppendResult(interp, "Failed to create parallelepiped", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Parallelepiped>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Parallelepiped>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Parallelepiped>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Parallelepiped>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Parallelepiped>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Parallelepiped>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Parallelepiped>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Parallelepiped>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                            Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Parallelepiped>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Parallelepiped>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Parallelepiped>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Parallelepiped>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Parallelepiped>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Parallelepiped>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Parallelepiped>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Parallelepiped>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Parallelepiped>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Parallelepiped>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Parallelepiped>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Parallelepiped>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Parallelepiped>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Parallelepiped>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Parallelepiped>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Parallelepiped>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Parallelepiped>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Parallelepiped>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Parallelepiped>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Parallelepiped>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Parallelepiped>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Parallelepiped>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Parallelepiped>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Parallelepiped>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Parallelepiped>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Parallelepiped>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Parallelepiped>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Parallelepiped>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+ParallelepipedWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                          Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Parallelepiped>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Parallelepiped>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec parallelepipedOps[] = {
+    {"add",       1, ParallelepipedAddOp, 12, 12, "v0x v0y v0z v1x v1y v1z v2x v2y v2z name"},
+    {"color",     2, ParallelepipedColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, ParallelepipedCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, ParallelepipedDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, ParallelepipedEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, ParallelepipedFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, ParallelepipedLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, ParallelepipedLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, ParallelepipedLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, ParallelepipedMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, ParallelepipedOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, ParallelepipedOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, ParallelepipedOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, ParallelepipedPositionOp, 5, 6, "x y z ?name?"},
+    {"scale",     2, ParallelepipedScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shading",   2, ParallelepipedShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, ParallelepipedVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, ParallelepipedWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nParallelepipedOps = NumCmdSpecs(parallelepipedOps);
+
+static int
+ParallelepipedCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nParallelepipedOps, parallelepipedOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+PolyDataAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<PolyData>(name)) {
+            Tcl_AppendResult(interp, "Failed to create polydata", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<PolyData>("all")) {
+            Tcl_AppendResult(interp, "Failed to create polydata for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataCloudStyleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    PolyData::CloudStyle style;
+    char *str =  Tcl_GetString(objv[2]);
+    if (str[0] == 'm' && strcmp(str, "mesh") == 0) {
+        style = PolyData::CLOUD_MESH;
+    } else if (str[0] == 'p' && strcmp(str, "points") == 0) {
+        style = PolyData::CLOUD_POINTS;
+    } else {
+        Tcl_AppendResult(interp, "bad cloudstyle option \"", str,
+                         "\": should be one of: 'mesh', 'points'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setPolyDataCloudStyle(name, style);
+    } else {
+        g_renderer->setPolyDataCloudStyle("all", style);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<PolyData>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<PolyData>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<PolyData>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<PolyData>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    PolyData::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = PolyData::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = PolyData::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = PolyData::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = PolyData::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = PolyData::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = PolyData::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == PolyData::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setPolyDataColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setPolyDataColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<PolyData>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<PolyData>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<PolyData>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<PolyData>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<PolyData>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<PolyData>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<PolyData>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<PolyData>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<PolyData>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<PolyData>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<PolyData>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<PolyData>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<PolyData>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<PolyData>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<PolyData>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<PolyData>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<PolyData>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<PolyData>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<PolyData>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<PolyData>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<PolyData>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<PolyData>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<PolyData>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<PolyData>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataPointSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    float size;
+    if (GetFloatFromObj(interp, objv[2], &size) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectPointSize<PolyData>(name, size);
+    } else {
+        g_renderer->setGraphicsObjectPointSize<PolyData>("all", size);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<PolyData>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<PolyData>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataPreInterpOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<PolyData>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<PolyData>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<PolyData>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<PolyData>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<PolyData>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<PolyData>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<PolyData>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<PolyData>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolyDataWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<PolyData>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<PolyData>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec polyDataOps[] = {
+    {"add",       1, PolyDataAddOp, 2, 3, "?dataSetName?"},
+    {"ccolor",    2, PolyDataColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"cloudstyle",2, PolyDataCloudStyleOp, 3, 4, "style ?dataSetName?"},
+    {"color",     5, PolyDataColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",  7, PolyDataColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode", 7, PolyDataColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"culling",   2, PolyDataCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, PolyDataDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",     1, PolyDataEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"lighting",  3, PolyDataLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor", 5, PolyDataLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth", 5, PolyDataLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"material",  1, PolyDataMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?dataSetName?"},
+    {"opacity",   2, PolyDataOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",    4, PolyDataOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"origin",    4, PolyDataOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       2, PolyDataPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"preinterp", 2, PolyDataPreInterpOp, 3, 4, "bool ?dataSetName?"},
+    {"ptsize",    2, PolyDataPointSizeOp, 3, 4, "size ?dataSetName?"},
+    {"scale",     2, PolyDataScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"shading",   2, PolyDataShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, PolyDataVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"wireframe", 1, PolyDataWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nPolyDataOps = NumCmdSpecs(polyDataOps);
+
+static int
+PolyDataCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nPolyDataOps, polyDataOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+PolygonAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    int numSides;
+    double center[3], normal[3], radius;
+    if (Tcl_GetIntFromObj(interp, objv[2], &numSides) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &center[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &center[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &center[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[6], &normal[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[7], &normal[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[8], &normal[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[9], &radius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[10]);
+    if (!g_renderer->addPolygon(name, numSides, center, normal, radius)) {
+        Tcl_AppendResult(interp, "Failed to create polygon", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Polygon>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Polygon>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Polygon>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Polygon>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Polygon>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Polygon>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Polygon>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Polygon>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Polygon>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Polygon>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Polygon>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Polygon>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Polygon>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Polygon>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Polygon>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Polygon>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Polygon>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Polygon>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Polygon>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Polygon>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Polygon>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Polygon>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Polygon>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Polygon>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Polygon>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Polygon>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Polygon>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Polygon>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Polygon>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Polygon>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Polygon>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Polygon>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Polygon>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Polygon>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Polygon>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Polygon>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PolygonWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Polygon>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Polygon>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec polygonOps[] = {
+    {"add",       1, PolygonAddOp, 11, 11, "numSides centerX centerY centerZ normalX normalY normalZ radius name"},
+    {"color",     1, PolygonColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, PolygonCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, PolygonDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, PolygonEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, PolygonFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, PolygonLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, PolygonLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, PolygonLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, PolygonMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, PolygonOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, PolygonOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, PolygonOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       2, PolygonPositionOp, 5, 6, "x y z ?name?"},
+    {"scale",     2, PolygonScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shading",   2, PolygonShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, PolygonVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, PolygonWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nPolygonOps = NumCmdSpecs(polygonOps);
+
+static int
+PolygonCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+           Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nPolygonOps, polygonOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+PseudoColorAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<PseudoColor>(name)) {
+            Tcl_AppendResult(interp, "Failed to create pseudocolor", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<PseudoColor>("all")) {
+            Tcl_AppendResult(interp, "Failed to create pseudocolor for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorCloudStyleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    PseudoColor::CloudStyle style;
+    char *str =  Tcl_GetString(objv[2]);
+    if (str[0] == 'm' && strcmp(str, "mesh") == 0) {
+        style = PseudoColor::CLOUD_MESH;
+    } else if (str[0] == 'p' && strcmp(str, "points") == 0) {
+        style = PseudoColor::CLOUD_POINTS;
+    } else if (str[0] == 's' && strcmp(str, "splat") == 0) {
+        style = PseudoColor::CLOUD_SPLAT;
+    } else {
+        Tcl_AppendResult(interp, "bad cloudstyle option \"", str,
+                         "\": should be one of: 'mesh', 'points', 'splat'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setPseudoColorCloudStyle(name, style);
+    } else {
+        g_renderer->setPseudoColorCloudStyle("all", style);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<PseudoColor>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<PseudoColor>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<PseudoColor>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<PseudoColor>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    PseudoColor::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = PseudoColor::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = PseudoColor::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = PseudoColor::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = PseudoColor::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = PseudoColor::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = PseudoColor::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == PseudoColor::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setPseudoColorColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setPseudoColorColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<PseudoColor>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<PseudoColor>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                            Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<PseudoColor>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<PseudoColor>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<PseudoColor>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<PseudoColor>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<PseudoColor>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<PseudoColor>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<PseudoColor>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<PseudoColor>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<PseudoColor>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<PseudoColor>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<PseudoColor>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<PseudoColor>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorPointSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    float size;
+    if (GetFloatFromObj(interp, objv[2], &size) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectPointSize<PseudoColor>(name, size);
+    } else {
+        g_renderer->setGraphicsObjectPointSize<PseudoColor>("all", size);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<PseudoColor>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<PseudoColor>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorPreInterpOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<PseudoColor>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<PseudoColor>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<PseudoColor>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<PseudoColor>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<PseudoColor>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<PseudoColor>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+PseudoColorWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<PseudoColor>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<PseudoColor>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec pseudoColorOps[] = {
+    {"add",          1, PseudoColorAddOp, 2, 3, "?dataSetName?"},
+    {"ccolor",       2, PseudoColorColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"cloudstyle",   2, PseudoColorCloudStyleOp, 3, 4, "style ?dataSetName?"},
+    {"color",        5, PseudoColorColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, PseudoColorColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, PseudoColorColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"delete",       1, PseudoColorDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, PseudoColorEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"lighting",     3, PseudoColorLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, PseudoColorLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, PseudoColorLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"opacity",      2, PseudoColorOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, PseudoColorOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          2, PseudoColorPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"preinterp",    2, PseudoColorPreInterpOp, 3, 4, "bool ?dataSetName?"},
+    {"ptsize",       2, PseudoColorPointSizeOp, 3, 4, "size ?dataSetName?"},
+    {"scale",        1, PseudoColorScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"visible",      1, PseudoColorVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"wireframe",    1, PseudoColorWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nPseudoColorOps = NumCmdSpecs(pseudoColorOps);
+
+static int
+PseudoColorCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nPseudoColorOps, pseudoColorOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+RendererClipPlaneOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    Axis axis;
+    if ((c == 'x') && (strcmp(string, "x") == 0)) {
+        axis = X_AXIS;
+    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
+        axis = Y_AXIS;
+    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
+        axis = Z_AXIS;
+    } else {
+        Tcl_AppendResult(interp, "bad clipplane option \"", string,
+                         "\": should be axisName ratio direction", (char*)NULL);
+        return TCL_ERROR;
+    }
+    double ratio;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    int direction;
+    if (Tcl_GetIntFromObj(interp, objv[4], &direction) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    g_renderer->setClipPlane(axis, ratio, direction);
+    return TCL_OK;
+}
+
+static int
+RendererDepthPeelingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    g_renderer->setUseDepthPeeling(state);
+    if (objc > 3) {
+        if (objc < 5) {
+             Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]),
+                              " ", Tcl_GetString(objv[1]), " bool ?occlusionRatio maxPeels?\"", (char*)NULL);
+            return TCL_ERROR;
+        }
+        double occlusionRatio;
+        int maxPeels;
+        if (Tcl_GetDoubleFromObj(interp, objv[3], &occlusionRatio) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (occlusionRatio < 0.0 || occlusionRatio > 0.5) {
+            Tcl_AppendResult(interp, "bad occlusionRatio value \"", Tcl_GetString(objv[3]),
+                             "\": should be [0,0.5]", (char*)NULL);
+            return TCL_ERROR;
+        }
+        if (Tcl_GetIntFromObj(interp, objv[4], &maxPeels) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (maxPeels < 0) {
+            Tcl_AppendResult(interp, "bad maxPeels value \"", Tcl_GetString(objv[3]),
+                             "\": must be zero or greater", (char*)NULL);
+            return TCL_ERROR;
+        }
+        g_renderer->setDepthPeelingParams(occlusionRatio, maxPeels);
+    }
+    return TCL_OK;
+}
+
+static int
+RendererLightsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    int idx;
+    bool state;
+    if (Tcl_GetIntFromObj(interp, objv[2], &idx) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    g_renderer->setLightSwitch(idx, state);
+    return TCL_OK;
+}
+
+static int
+RendererTwoSidedLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                           Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    g_renderer->setUseTwoSidedLighting(state);
+    return TCL_OK;
+}
+
+static int
+RendererRenderOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    g_renderer->eventuallyRender();
+    return TCL_OK;
+}
+
+static CmdSpec rendererOps[] = {
+    {"clipplane",  1, RendererClipPlaneOp, 5, 5, "axis ratio direction"},
+    {"depthpeel",  1, RendererDepthPeelingOp, 3, 5, "bool ?occlusionRatio maxPeels?"},
+    {"light2side", 6, RendererTwoSidedLightingOp, 3, 3, "bool"},
+    {"lights",     6, RendererLightsOp, 4, 4, "idx bool"}, 
+    {"render",     1, RendererRenderOp, 2, 2, ""}
+};
+static int nRendererOps = NumCmdSpecs(rendererOps);
+
+static int
+RendererCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nRendererOps, rendererOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+ScreenBgColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->setBackgroundColor(color);
+    return TCL_OK;
+}
+
+static int
+ScreenSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    int width, height;
+
+    if (Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    g_renderer->setWindowSize(width, height);
+    return TCL_OK;
+}
+
+static CmdSpec screenOps[] = {
+    {"bgcolor", 1, ScreenBgColorOp, 5, 5, "r g b"},
+    {"size", 1, ScreenSizeOp, 4, 4, "width height"}
+};
+static int nScreenOps = NumCmdSpecs(screenOps);
+
+static int
+ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nScreenOps, screenOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+SphereAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double center[3], radius;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &center[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &center[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &center[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &radius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *name = Tcl_GetString(objv[6]);
+    if (!g_renderer->addSphere(name, center, radius)) {
+        Tcl_AppendResult(interp, "Failed to create sphere", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+SphereDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Sphere>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Sphere>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+SphereColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Sphere>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Sphere>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereCullingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectCulling<Sphere>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectCulling<Sphere>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Sphere>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Sphere>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereFlipNormalsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectFlipNormals<Sphere>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectFlipNormals<Sphere>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Sphere>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Sphere>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Sphere>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Sphere>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Sphere>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Sphere>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereMaterialOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double ambient, diffuse, specCoeff, specPower;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &ambient) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &diffuse) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &specCoeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &specPower) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectAmbient<Sphere>(name, ambient);
+        g_renderer->setGraphicsObjectDiffuse<Sphere>(name, diffuse);
+        g_renderer->setGraphicsObjectSpecular<Sphere>(name, specCoeff, specPower);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Sphere>("all", ambient);
+        g_renderer->setGraphicsObjectDiffuse<Sphere>("all", diffuse);
+        g_renderer->setGraphicsObjectSpecular<Sphere>("all", specCoeff, specPower);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Sphere>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Sphere>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Sphere>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Sphere>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Sphere>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Sphere>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+SpherePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Sphere>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Sphere>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereResolutionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    int theta, phi;
+    if (Tcl_GetIntFromObj(interp, objv[2], &theta) != TCL_OK ||
+        Tcl_GetIntFromObj(interp, objv[3], &phi) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setSphereResolution(name, theta, phi);
+    } else {
+        g_renderer->setSphereResolution("all", theta, phi);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Sphere>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Sphere>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereSectionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double thetaStart, thetaEnd, phiStart, phiEnd;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &thetaStart) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &thetaEnd) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &phiStart) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &phiEnd) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setSphereSection(name, thetaStart, thetaEnd, phiStart, phiEnd);
+    } else {
+        g_renderer->setSphereSection("all", thetaStart, thetaEnd, phiStart, phiEnd);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    GraphicsObject::ShadingModel shadeModel;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'f' && strcmp(str, "flat") == 0) {
+        shadeModel = GraphicsObject::SHADE_FLAT;
+    } else if (str[0] == 's' && strcmp(str, "smooth") == 0) {
+        shadeModel = GraphicsObject::SHADE_GOURAUD;
+    } else {
+         Tcl_AppendResult(interp, "bad shading option \"", str,
+                         "\": should be one of: 'flat', 'smooth'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectShadingModel<Sphere>(name, shadeModel);
+    } else {
+        g_renderer->setGraphicsObjectShadingModel<Sphere>("all", shadeModel);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Sphere>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Sphere>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+SphereWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Sphere>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Sphere>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec sphereOps[] = {
+    {"add",       1, SphereAddOp, 7, 7, "centerX centerY centerZ radius name"},
+    {"color",     2, SphereColorOp, 5, 6, "r g b ?name?"},
+    {"culling",   2, SphereCullingOp, 3, 4, "bool ?name?"},
+    {"delete",    1, SphereDeleteOp, 2, 3, "?name?"},
+    {"edges",     1, SphereEdgeVisibilityOp, 3, 4, "bool ?name?"},
+    {"flipnorms", 1, SphereFlipNormalsOp, 3, 4, "bool ?name?"},
+    {"lighting",  3, SphereLightingOp, 3, 4, "bool ?name?"},
+    {"linecolor", 5, SphereLineColorOp, 5, 6, "r g b ?name?"},
+    {"linewidth", 5, SphereLineWidthOp, 3, 4, "width ?name?"},
+    {"material",  1, SphereMaterialOp, 6, 7, "ambientCoeff diffuseCoeff specularCoeff specularPower ?name?"},
+    {"opacity",   2, SphereOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, SphereOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, SphereOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       2, SpherePositionOp, 5, 6, "x y z ?name?"},
+    {"resolution",1, SphereResolutionOp, 4, 5, "thetaRes phiRes ?name?"},
+    {"scale",     2, SphereScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"section",   2, SphereSectionOp, 6, 7, "thetaStart thetaEnd phiStart phiEnd ?name?"},
+    {"shading",   2, SphereShadingOp, 3, 4, "val ?name?"},
+    {"visible",   1, SphereVisibleOp, 3, 4, "bool ?name?"},
+    {"wireframe", 1, SphereWireframeOp, 3, 4, "bool ?name?"}
+};
+static int nSphereOps = NumCmdSpecs(sphereOps);
+
+static int
+SphereCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nSphereOps, sphereOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+StreamlinesAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<Streamlines>(name)) {
+            Tcl_AppendResult(interp, "Failed to create streamlines", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<Streamlines>("all")) {
+            Tcl_AppendResult(interp, "Failed to create streamlines for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Streamlines>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Streamlines>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Streamlines>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Streamlines>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    Streamlines::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Streamlines::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Streamlines::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = Streamlines::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = Streamlines::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = Streamlines::COLOR_BY_VECTOR_Z;
+    } else if (str[0] == 'c' &&
+               (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = Streamlines::COLOR_CONSTANT;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == Streamlines::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *dataSetName = Tcl_GetString(objv[4]);
+        g_renderer->setStreamlinesColorMode(dataSetName, mode, fieldName);
+    } else {
+        g_renderer->setStreamlinesColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Streamlines>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Streamlines>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                            Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Streamlines>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Streamlines>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesLengthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double length;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &length) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setStreamlinesLength(name, length);
+    } else {
+        g_renderer->setStreamlinesLength("all", length);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Streamlines>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Streamlines>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Streamlines>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Streamlines>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesLinesOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->setStreamlinesTypeToLines(name);
+    } else {
+        g_renderer->setStreamlinesTypeToLines("all");
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Streamlines>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Streamlines>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Streamlines>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Streamlines>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                    Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Streamlines>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Streamlines>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Streamlines>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Streamlines>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Streamlines>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Streamlines>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesRibbonsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    double width, angle;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &angle) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setStreamlinesTypeToRibbons(name, width, angle);
+    } else {
+        g_renderer->setStreamlinesTypeToRibbons("all", width, angle);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[3], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[5], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setStreamlinesSeedColor(name, color);
+    } else {
+        g_renderer->setStreamlinesSeedColor("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedDiskOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double center[3], normal[3], radius, innerRadius;
+    int numPoints;
+    for (int i = 0; i < 3; i++) {
+        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &center[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &normal[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[9], &radius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[10], &innerRadius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIntFromObj(interp, objv[11], &numPoints) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 13) {
+        const char *name = Tcl_GetString(objv[12]);
+        g_renderer->setStreamlinesSeedToDisk(name, center, normal, radius, innerRadius, numPoints);
+    } else {
+        g_renderer->setStreamlinesSeedToDisk("all", center, normal, radius, innerRadius, numPoints);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedFilledMeshOp(ClientData clientData, Tcl_Interp *interp,
+                            int objc, Tcl_Obj *const *objv)
+{
+    int numPoints = 0;
+    if (Tcl_GetIntFromObj(interp, objv[3], &numPoints) != TCL_OK ||
+        numPoints < 1) {
+        return TCL_ERROR;
+    }
+    const char *string = Tcl_GetString(objv[4]);
+    char c = string[0];
+    if ((c != 'd') || (strcmp(string, "data") != 0)) {
+        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
+                         string, "\": should be data", (char*)NULL);
+        return TCL_ERROR;
+    }
+    string = Tcl_GetString(objv[5]);
+    c = string[0];
+    if ((c != 'f') || (strcmp(string, "follows") != 0)) {
+        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
+                         string, "\": should be follows", (char*)NULL);
+        return TCL_ERROR;
+    }
+    int nbytes = 0;
+    if (Tcl_GetIntFromObj(interp, objv[6], &nbytes) != TCL_OK ||
+        nbytes < 0) {
+        return TCL_ERROR;
+    }
+    char *data = (char *)malloc(nbytes);
+    if (!SocketRead(data, nbytes)) {
+        free(data);
+        return TCL_ERROR;
+    }
+    if (objc == 8) {
+        const char *name = Tcl_GetString(objv[7]);
+        if (!g_renderer->setStreamlinesSeedToFilledMesh(name, data, nbytes,
+                                                        numPoints)) {
+            free(data);
+            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->setStreamlinesSeedToFilledMesh("all", data, nbytes,
+                                                        numPoints)) {
+            free(data);
+            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    free(data);
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedMeshPointsOp(ClientData clientData, Tcl_Interp *interp,
+                            int objc, Tcl_Obj *const *objv)
+{
+    const char *string = Tcl_GetString(objv[3]);
+    char c = string[0];
+    if ((c != 'd') || (strcmp(string, "data") != 0)) {
+        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
+                         string, "\": should be data", (char*)NULL);
+        return TCL_ERROR;
+    }
+    string = Tcl_GetString(objv[4]);
+    c = string[0];
+    if ((c != 'f') || (strcmp(string, "follows") != 0)) {
+        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
+                         string, "\": should be follows", (char*)NULL);
+        return TCL_ERROR;
+    }
+    int nbytes = 0;
+    if (Tcl_GetIntFromObj(interp, objv[5], &nbytes) != TCL_OK ||
+        nbytes < 0) {
+        return TCL_ERROR;
+    }
+    char *data = (char *)malloc(nbytes);
+    if (!SocketRead(data, nbytes)) {
+        free(data);
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        if (!g_renderer->setStreamlinesSeedToMeshPoints(name, data, nbytes)) {
+            free(data);
+            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->setStreamlinesSeedToMeshPoints("all", data, nbytes)) {
+            free(data);
+            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    free(data);
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedNumPointsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                           Tcl_Obj *const *objv)
+{
+    int numPoints;
+    if (Tcl_GetIntFromObj(interp, objv[3], &numPoints) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setStreamlinesNumberOfSeedPoints(name, numPoints);
+    } else {
+        g_renderer->setStreamlinesNumberOfSeedPoints("all", numPoints);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedPointsOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setStreamlinesSeedToMeshPoints(name);
+    } else {
+        g_renderer->setStreamlinesSeedToMeshPoints("all");
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    double center[3], normal[3], angle, radius;
+    int numSides;
+    for (int i = 0; i < 3; i++) {
+        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &center[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &normal[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[9], &angle) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[10], &radius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIntFromObj(interp, objv[11], &numSides) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (numSides < 3) {
+        Tcl_AppendResult(interp, "numSides must be 3 or greater", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 13) {
+        const char *name = Tcl_GetString(objv[12]);
+        g_renderer->setStreamlinesSeedToPolygon(name, center, normal, angle, radius, numSides);
+    } else {
+        g_renderer->setStreamlinesSeedToPolygon("all", center, normal, angle, radius, numSides);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedFilledPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                               Tcl_Obj *const *objv)
+{
+    double center[3], normal[3], angle, radius;
+    int numSides, numPoints;
+    for (int i = 0; i < 3; i++) {
+        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &center[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &normal[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[9], &angle) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[10], &radius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIntFromObj(interp, objv[11], &numSides) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (numSides < 3) {
+        Tcl_AppendResult(interp, "numSides must be 3 or greater", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (Tcl_GetIntFromObj(interp, objv[12], &numPoints) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 14) {
+        const char *name = Tcl_GetString(objv[13]);
+        g_renderer->setStreamlinesSeedToFilledPolygon(name, center, normal, angle,
+                                                      radius, numSides, numPoints);
+    } else {
+        g_renderer->setStreamlinesSeedToFilledPolygon("all", center, normal, angle,
+                                                      radius, numSides, numPoints);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedPointSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                           Tcl_Obj *const *objv)
+{
+    float size;
+    if (GetFloatFromObj(interp, objv[3], &size) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setStreamlinesSeedPointSize(name, size);
+    } else {
+        g_renderer->setStreamlinesSeedPointSize("all", size);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedRakeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                      Tcl_Obj *const *objv)
+{
+    double start[3], end[3];
+    int numPoints;
+    for (int i = 0; i < 3; i++) {
+        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &start[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &end[i]) != TCL_OK) {
+            return TCL_ERROR;
+        }
+    }
+    if (Tcl_GetIntFromObj(interp, objv[9], &numPoints) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 11) {
+        const char *name = Tcl_GetString(objv[10]);
+        g_renderer->setStreamlinesSeedToRake(name, start, end, numPoints);
+    } else {
+        g_renderer->setStreamlinesSeedToRake("all", start, end, numPoints);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedRandomOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    int numPoints;
+    if (Tcl_GetIntFromObj(interp, objv[3], &numPoints) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setStreamlinesSeedToFilledMesh(name, numPoints);
+    } else {
+        g_renderer->setStreamlinesSeedToFilledMesh("all", numPoints);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesSeedVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                         Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setStreamlinesSeedVisibility(name, state);
+    } else {
+        g_renderer->setStreamlinesSeedVisibility("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec streamlinesSeedOps[] = {
+    {"color",   1, StreamlinesSeedColorOp,         6, 7, "r g b ?dataSetName?"},
+    {"disk",    1, StreamlinesSeedDiskOp,          12, 13, "centerX centerY centerZ normalX normalY normalZ radius innerRadius numPoints ?dataSetName?"},
+    {"fmesh",   2, StreamlinesSeedFilledMeshOp,    7, 8, "numPoints data follows nbytes ?dataSetName?"},
+    {"fpoly",   2, StreamlinesSeedFilledPolygonOp, 13, 14, "centerX centerY centerZ normalX normalY normalZ angle radius numSides numPoints ?dataSetName?"},
+    {"mesh",    1, StreamlinesSeedMeshPointsOp,    6, 7, "data follows nbytes ?dataSetName?"},
+    {"numpts",  1, StreamlinesSeedNumPointsOp,     4, 5, "numPoints ?dataSetName?"},
+    {"points",  3, StreamlinesSeedPointsOp,        3, 4, "?dataSetName?"},
+    {"polygon", 3, StreamlinesSeedPolygonOp,       12, 13, "centerX centerY centerZ normalX normalY normalZ angle radius numSides ?dataSetName?"},
+    {"ptsize",  2, StreamlinesSeedPointSizeOp,     3, 4, "size ?dataSetName?"},
+    {"rake",    3, StreamlinesSeedRakeOp,          10, 11, "startX startY startZ endX endY endZ numPoints ?dataSetName?"},
+    {"random",  3, StreamlinesSeedRandomOp,        4, 5, "numPoints ?dataSetName?"},
+    {"visible", 1, StreamlinesSeedVisibleOp,       4, 5, "bool ?dataSetName?"}
+};
+static int nStreamlinesSeedOps = NumCmdSpecs(streamlinesSeedOps);
+
+static int
+StreamlinesSeedOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nStreamlinesSeedOps, streamlinesSeedOps,
+                        CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+StreamlinesTerminalSpeedOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                           Tcl_Obj *const *objv)
+{
+    double speed;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &speed) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setStreamlinesTerminalSpeed(name, speed);
+    } else {
+        g_renderer->setStreamlinesTerminalSpeed("all", speed);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesTubesOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    int numSides;
+    double radius;
+    if (Tcl_GetIntFromObj(interp, objv[2], &numSides) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &radius) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setStreamlinesTypeToTubes(name, numSides, radius);
+    } else {
+        g_renderer->setStreamlinesTypeToTubes("all", numSides, radius);
+    }
+    return TCL_OK;
+}
+
+static int
+StreamlinesVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Streamlines>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Streamlines>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec streamlinesOps[] = {
+    {"add",          1, StreamlinesAddOp,            2, 3, "?dataSetName?"},
+    {"ccolor",       2, StreamlinesColorOp,          5, 6, "r g b ?dataSetName?"},
+    {"color",        5, StreamlinesColorOp,          5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, StreamlinesColorMapOp,       3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, StreamlinesColorModeOp,      4, 5, "mode fieldName ?dataSetName?"},
+    {"delete",       1, StreamlinesDeleteOp,         2, 3, "?dataSetName?"},
+    {"edges",        1, StreamlinesEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"length",       2, StreamlinesLengthOp,         3, 4, "length ?dataSetName?"},
+    {"lighting",     3, StreamlinesLightingOp,       3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, StreamlinesLineColorOp,      5, 6, "r g b ?dataSetName?"},
+    {"lines",        5, StreamlinesLinesOp,          2, 3, "?dataSetName?"},
+    {"linewidth",    5, StreamlinesLineWidthOp,      3, 4, "width ?dataSetName?"},
+    {"opacity",      2, StreamlinesOpacityOp,        3, 4, "val ?dataSetName?"},
+    {"orient",       2, StreamlinesOrientOp,         6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          1, StreamlinesPositionOp,       5, 6, "x y z ?dataSetName?"},
+    {"ribbons",      1, StreamlinesRibbonsOp,        4, 5, "width angle ?dataSetName?"},
+    {"scale",        2, StreamlinesScaleOp,          5, 6, "sx sy sz ?dataSetName?"},
+    {"seed",         2, StreamlinesSeedOp,           3, 14, "op params... ?dataSetName?"},
+    {"termspeed",    2, StreamlinesTerminalSpeedOp,  3, 4, "speed ?dataSetName?"},
+    {"tubes",        2, StreamlinesTubesOp,          4, 5, "numSides radius ?dataSetName?"},
+    {"visible",      1, StreamlinesVisibleOp,        3, 4, "bool ?dataSetName?"}
+};
+static int nStreamlinesOps = NumCmdSpecs(streamlinesOps);
+
+static int
+StreamlinesCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nStreamlinesOps, streamlinesOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+Text3DAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    int fontSize;
+    const char *fontFamily = Tcl_GetString(objv[2]);
+    if (Tcl_GetIntFromObj(interp, objv[3], &fontSize) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    const char *text = Tcl_GetString(objv[4]);
+    const char *name = Tcl_GetString(objv[5]);
+    if (!g_renderer->addText3D(name, text, fontFamily, fontSize)) {
+        Tcl_AppendResult(interp, "Failed to create text3d", (char*)NULL);
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DBoldOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setText3DBold(name, state);
+    } else {
+        g_renderer->setText3DBold("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Text3D>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Text3D>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Text3D>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Text3D>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DFollowCameraOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setText3DFollowCamera(name, state);
+    } else {
+        g_renderer->setText3DFollowCamera("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DFontFamilyOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    const char *fontFamily = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setText3DFont(name, fontFamily);
+    } else {
+        g_renderer->setText3DFont("all", fontFamily);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DFontSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    int fontSize;
+    if (Tcl_GetIntFromObj(interp, objv[2], &fontSize) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setText3DFontSize(name, fontSize);
+    } else {
+        g_renderer->setText3DFontSize("all", fontSize);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DItalicOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setText3DItalic(name, state);
+    } else {
+        g_renderer->setText3DItalic("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Text3D>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Text3D>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Text3D>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Text3D>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DOriginOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double origin[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &origin[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &origin[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &origin[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectOrigin<Text3D>(name, origin);
+    } else {
+        g_renderer->setGraphicsObjectOrigin<Text3D>("all", origin);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Text3D>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Text3D>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Text3D>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Text3D>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DShadowOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setText3DShadow(name, state);
+    } else {
+        g_renderer->setText3DShadow("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DTextOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    char *str = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setText3DText(name, str);
+    } else {
+        g_renderer->setText3DText("all", str);
+    }
+    return TCL_OK;
+}
+
+static int
+Text3DVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Text3D>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Text3D>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec text3DOps[] = {
+    {"add",       1, Text3DAddOp, 6, 6, "font size text name"},
+    {"bold",      1, Text3DBoldOp, 3, 4, "bool ?name?"},
+    {"color",     1, Text3DColorOp, 5, 6, "r g b ?name?"},
+    {"delete",    1, Text3DDeleteOp, 2, 3, "?name?"},
+    {"fntfamily", 4, Text3DFontFamilyOp, 3, 4, "font ?name?"},
+    {"fntsize",   4, Text3DFontSizeOp, 3, 4, "size ?name?"},
+    {"follow",    2, Text3DFollowCameraOp, 3, 4, "bool ?name?"},
+    {"italic",    1, Text3DItalicOp, 3, 4, "bool ?name?"},
+    {"opacity",   2, Text3DOpacityOp, 3, 4, "value ?name?"},
+    {"orient",    4, Text3DOrientOp, 6, 7, "qw qx qy qz ?name?"},
+    {"origin",    4, Text3DOriginOp, 5, 6, "x y z ?name?"},
+    {"pos",       1, Text3DPositionOp, 5, 6, "x y z ?name?"},
+    {"scale",     1, Text3DScaleOp, 5, 6, "sx sy sz ?name?"},
+    {"shadow",    1, Text3DShadowOp, 3, 4, "bool ?name?"},
+    {"text",      1, Text3DTextOp, 3, 4, "text ?name?"},
+    {"visible",   1, Text3DVisibleOp, 3, 4, "bool ?name?"},
+};
+static int nText3DOps = NumCmdSpecs(text3DOps);
+
+static int
+Text3DCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nText3DOps, text3DOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+VolumeAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<Volume>(name)) {
+            Tcl_AppendResult(interp, "Failed to create volume", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<Volume>("all")) {
+            Tcl_AppendResult(interp, "Failed to create volume for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeBlendModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                  Tcl_Obj *const *objv)
+{
+    Volume::BlendMode mode;
+    const char *string = Tcl_GetString(objv[2]);
+    char c = string[0];
+    if ((c == 'c') && (strcmp(string, "composite") == 0)) {
+        mode = Volume::BLEND_COMPOSITE;
+    } else if ((c == 'm') && (strcmp(string, "max_intensity") == 0)) {
+        mode = Volume::BLEND_MAX_INTENSITY;
+    } else if ((c == 'm') && (strcmp(string, "min_intensity") == 0)) {
+        mode = Volume::BLEND_MIN_INTENSITY;
+    } else if ((c == 'a') && (strcmp(string, "additive") == 0)) {
+        mode = Volume::BLEND_ADDITIVE;
+    } else {
+        Tcl_AppendResult(interp, "bad blendmode option \"", string,
+                         "\": should be copmosite, max_intensity, min_intensity, or additive", (char*)NULL);
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setVolumeBlendMode(dataSetName, mode);
+    } else {
+        g_renderer->setVolumeBlendMode("all", mode);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Volume>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Volume>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Volume>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Volume>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Volume>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Volume>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Volume>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Volume>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Volume>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Volume>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumePositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                 Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Volume>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Volume>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeSampleRateOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double quality;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quality) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (quality < 0.0 || quality > 1.0) {
+        Tcl_AppendResult(interp, "bad volume quality value \"",
+                         Tcl_GetString(objv[2]),
+                         "\": should be in the range [0,1]", (char*)NULL);
+        return TCL_ERROR;
+    }
+    double distance;
+    double maxFactor = 4.0;
+#if 1
+    distance = 1.0 / (quality * (maxFactor - 1.0) + 1.0);
+#else
+    if (quality >= 0.5) {
+        distance = 1.0 / ((quality - 0.5) * (maxFactor - 1.0) * 2.0 + 1.0);
+    } else {
+        distance = ((0.5 - quality) * (maxFactor - 1.0) * 2.0 + 1.0);
+    }
+#endif
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setVolumeSampleDistance(name, distance);
+    } else {
+        g_renderer->setVolumeSampleDistance("all", distance);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Volume>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Volume>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeShadingAmbientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double coeff;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &coeff) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGraphicsObjectAmbient<Volume>(name, coeff);
+    } else {
+        g_renderer->setGraphicsObjectAmbient<Volume>("all", coeff);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeShadingDiffuseOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                       Tcl_Obj *const *objv)
+{
+    double coeff;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &coeff) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setGraphicsObjectDiffuse<Volume>(name, coeff);
+    } else {
+        g_renderer->setGraphicsObjectDiffuse<Volume>("all", coeff);
+    }
+    return TCL_OK;
+}
+
+static int
+VolumeShadingSpecularOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
+{
+    double coeff, power;
+    if (Tcl_GetDoubleFromObj(interp, objv[3], &coeff) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &power) != TCL_OK) {
+        return TCL_ERROR;
+    }
+
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectSpecular<Volume>(name, coeff, power);
+    } else {
+        g_renderer->setGraphicsObjectSpecular<Volume>("all", coeff, power);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec volumeShadingOps[] = {
+    {"ambient",  1, VolumeShadingAmbientOp,  4, 5, "coeff ?dataSetName?"},
+    {"diffuse",  1, VolumeShadingDiffuseOp,  4, 5, "coeff ?dataSetName?"},
+    {"specular", 1, VolumeShadingSpecularOp, 5, 6, "coeff power ?dataSetName?"}
+};
+static int nVolumeShadingOps = NumCmdSpecs(volumeShadingOps);
+
+static int
+VolumeShadingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nVolumeShadingOps, volumeShadingOps,
+                        CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+VolumeVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Volume>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Volume>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec volumeOps[] = {
+    {"add",          1, VolumeAddOp,        2, 3, "?dataSetName?"},
+    {"blendmode",    1, VolumeBlendModeOp,  3, 4, "blendMode ?dataSetName?"},
+    {"colormap",     1, VolumeColorMapOp,   3, 4, "colorMapName ?dataSetName?"},
+    {"delete",       1, VolumeDeleteOp,     2, 3, "?dataSetName?"},
+    {"lighting",     1, VolumeLightingOp,   3, 4, "bool ?dataSetName?"},
+    {"opacity",      2, VolumeOpacityOp,    3, 4, "val ?dataSetName?"},
+    {"orient",       2, VolumeOrientOp,     6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          1, VolumePositionOp,   5, 6, "x y z ?dataSetName?"},
+    {"quality",      1, VolumeSampleRateOp, 3, 4, "val ?dataSetName?"},
+    {"scale",        2, VolumeScaleOp,      5, 6, "sx sy sz ?dataSetName?"},
+    {"shading",      2, VolumeShadingOp,    4, 6, "oper val ?dataSetName?"},
+    {"visible",      1, VolumeVisibleOp,    3, 4, "bool ?dataSetName?"}
+};
+static int nVolumeOps = NumCmdSpecs(volumeOps);
+
+static int
+VolumeCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nVolumeOps, volumeOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+static int
+WarpAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+          Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        if (!g_renderer->addGraphicsObject<Warp>(name)) {
+            Tcl_AppendResult(interp, "Failed to create warp", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addGraphicsObject<Warp>("all")) {
+            Tcl_AppendResult(interp, "Failed to create warp for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
+    }
+    return TCL_OK;
+}
+
+static int
+WarpCloudStyleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    Warp::CloudStyle style;
+    char *str =  Tcl_GetString(objv[2]);
+    if (str[0] == 'm' && strcmp(str, "mesh") == 0) {
+        style = Warp::CLOUD_MESH;
+    } else if (str[0] == 'p' && strcmp(str, "points") == 0) {
+        style = Warp::CLOUD_POINTS;
+    } else {
+        Tcl_AppendResult(interp, "bad cloudstyle option \"", str,
+                         "\": should be one of: 'mesh', 'points'", (char*)NULL);
+        return TCL_ERROR;
+    }
+
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setWarpCloudStyle(name, style);
+    } else {
+        g_renderer->setWarpCloudStyle("all", style);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectColor<Warp>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectColor<Warp>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    const char *colorMapName = Tcl_GetString(objv[2]);
+    if (objc == 4) {
+        const char *dataSetName = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectColorMap<Warp>(dataSetName, colorMapName);
+    } else {
+        g_renderer->setGraphicsObjectColorMap<Warp>("all", colorMapName);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    Warp::ColorMode mode;
+    const char *str = Tcl_GetString(objv[2]);
+    if (str[0] == 'c' &&
+        (strcmp(str, "ccolor") == 0 || strcmp(str, "constant") == 0)) {
+        mode = Warp::COLOR_CONSTANT;
+    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
+        mode = Warp::COLOR_BY_SCALAR;
+    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
+        mode = Warp::COLOR_BY_VECTOR_MAGNITUDE;
+    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
+        mode = Warp::COLOR_BY_VECTOR_X;
+    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
+        mode = Warp::COLOR_BY_VECTOR_Y;
+    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
+        mode = Warp::COLOR_BY_VECTOR_Z;
+    } else {
+        Tcl_AppendResult(interp, "bad color mode option \"", str,
+                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor', 'constant'", (char*)NULL);
+        return TCL_ERROR;
+    }
+    const char *fieldName = Tcl_GetString(objv[3]);
+    if (mode == Warp::COLOR_CONSTANT) {
+        fieldName = NULL;
+    }
+    if (objc == 5) {
+        const char *name = Tcl_GetString(objv[4]);
+        g_renderer->setWarpColorMode(name, mode, fieldName);
+    } else {
+        g_renderer->setWarpColorMode("all", mode, fieldName);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    if (objc == 3) {
+        const char *name = Tcl_GetString(objv[2]);
+        g_renderer->deleteGraphicsObject<Warp>(name);
+    } else {
+        g_renderer->deleteGraphicsObject<Warp>("all");
+    }
+    return TCL_OK;
+}
+
+static int
+WarpEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                     Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeVisibility<Warp>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectEdgeVisibility<Warp>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpLightingOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectLighting<Warp>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectLighting<Warp>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float color[3];
+    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
+        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectEdgeColor<Warp>(name, color);
+    } else {
+        g_renderer->setGraphicsObjectEdgeColor<Warp>("all", color);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float width;
+    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectEdgeWidth<Warp>(name, width);
+    } else {
+        g_renderer->setGraphicsObjectEdgeWidth<Warp>("all", width);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
+{
+    double opacity;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectOpacity<Warp>(name, opacity);
+    } else {
+        g_renderer->setGraphicsObjectOpacity<Warp>("all", opacity);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    double quat[4];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 7) {
+        const char *name = Tcl_GetString(objv[6]);
+        g_renderer->setGraphicsObjectOrientation<Warp>(name, quat);
+    } else {
+        g_renderer->setGraphicsObjectOrientation<Warp>("all", quat);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpPointSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    float size;
+    if (GetFloatFromObj(interp, objv[2], &size) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectPointSize<Warp>(name, size);
+    } else {
+        g_renderer->setGraphicsObjectPointSize<Warp>("all", size);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+               Tcl_Obj *const *objv)
+{
+    double pos[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectPosition<Warp>(name, pos);
+    } else {
+        g_renderer->setGraphicsObjectPosition<Warp>("all", pos);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpPreInterpOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<Warp>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectInterpolateBeforeMapping<Warp>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+            Tcl_Obj *const *objv)
+{
+    double scale[3];
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
+        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 6) {
+        const char *name = Tcl_GetString(objv[5]);
+        g_renderer->setGraphicsObjectScale<Warp>(name, scale);
+    } else {
+        g_renderer->setGraphicsObjectScale<Warp>("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+              Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectVisibility<Warp>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectVisibility<Warp>("all", state);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpWarpScaleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    double scale;
+    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setWarpWarpScale(name, scale);
+    } else {
+        g_renderer->setWarpWarpScale("all", scale);
+    }
+    return TCL_OK;
+}
+
+static int
+WarpWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                Tcl_Obj *const *objv)
+{
+    bool state;
+    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (objc == 4) {
+        const char *name = Tcl_GetString(objv[3]);
+        g_renderer->setGraphicsObjectWireframe<Warp>(name, state);
+    } else {
+        g_renderer->setGraphicsObjectWireframe<Warp>("all", state);
+    }
+    return TCL_OK;
+}
+
+static CmdSpec warpOps[] = {
+    {"add",          1, WarpAddOp, 2, 3, "?dataSetName?"},
+    {"ccolor",       2, WarpColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"cloudstyle",   2, WarpCloudStyleOp, 3, 4, "style ?dataSetName?"},
+    {"color",        5, WarpColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"colormap",     7, WarpColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"colormode",    7, WarpColorModeOp, 4, 5, "mode fieldName ?dataSetName?"},
+    {"delete",       1, WarpDeleteOp, 2, 3, "?dataSetName?"},
+    {"edges",        1, WarpEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
+    {"lighting",     3, WarpLightingOp, 3, 4, "bool ?dataSetName?"},
+    {"linecolor",    5, WarpLineColorOp, 5, 6, "r g b ?dataSetName?"},
+    {"linewidth",    5, WarpLineWidthOp, 3, 4, "width ?dataSetName?"},
+    {"opacity",      2, WarpOpacityOp, 3, 4, "value ?dataSetName?"},
+    {"orient",       2, WarpOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
+    {"pos",          2, WarpPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"preinterp",    2, WarpPreInterpOp, 3, 4, "bool ?dataSetName?"},
+    {"ptsize",       2, WarpPointSizeOp, 3, 4, "size ?dataSetName?"},
+    {"scale",        1, WarpScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"visible",      1, WarpVisibleOp, 3, 4, "bool ?dataSetName?"},
+    {"warpscale",    2, WarpWarpScaleOp, 3, 4, "value ?dataSetName?"},
+    {"wireframe",    2, WarpWireframeOp, 3, 4, "bool ?dataSetName?"}
+};
+static int nWarpOps = NumCmdSpecs(warpOps);
+
+static int
+WarpCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+             Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = GetOpFromObj(interp, nWarpOps, warpOps,
+                        CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+/**
+ * \brief Execute commands from client in Tcl interpreter
+ * 
+ * In this threaded model, the select call is for event compression.  We
+ * want to execute render server commands as long as they keep coming.  
+ * This lets us execute a stream of many commands but render once.  This 
+ * benefits camera movements, screen resizing, and opacity changes 
+ * (using a slider on the client).  The down side is you don't render
+ * until there's a lull in the command stream.  If the client needs an
+ * image, it can issue an "imgflush" command.  That breaks us out of the
+ * read loop.
+ */
+int
+VtkVis::processCommands(Tcl_Interp *interp,
+                        ClientData clientData,
+                        ReadBuffer *inBufPtr, 
+                        int fdOut)
+{
+    int ret = 1;
+    int status = TCL_OK;
+
+    Tcl_DString command;
+    Tcl_DStringInit(&command);
+    fd_set readFds;
+    struct timeval tv, *tvPtr;
+
+    FD_ZERO(&readFds);
+    FD_SET(inBufPtr->file(), &readFds);
+    tvPtr = NULL;                       /* Wait for the first read. This is so
+                                         * that we don't spin when no data is
+                                         * available. */
+    while (inBufPtr->isLineAvailable() || 
+           (select(inBufPtr->file()+1, &readFds, NULL, NULL, tvPtr) > 0)) {
+        size_t numBytes;
+        unsigned char *buffer;
+
+        /* A short read is treated as an error here because we assume that we
+         * will always get commands line by line. */
+        if (inBufPtr->getLine(&numBytes, &buffer) != ReadBuffer::OK) {
+            /* Terminate the server if we can't communicate with the client
+             * anymore. */
+            if (inBufPtr->status() == ReadBuffer::ENDFILE) {
+                TRACE("Exiting server on EOF from client");
+                return -1;
+            } else {
+                ERROR("Exiting server, failed to read from client: %s",
+                      strerror(errno));
+                return -1;
+            }
+        }
+        Tcl_DStringAppend(&command, (char *)buffer, numBytes);
+        if (Tcl_CommandComplete(Tcl_DStringValue(&command))) {
+            struct timeval start, finish;
+            gettimeofday(&start, NULL);
+            status = ExecuteCommand(interp, &command);
+            gettimeofday(&finish, NULL);
+            g_stats.cmdTime += (MSECS_ELAPSED(start, finish) / 1.0e+3);
+            g_stats.nCommands++;
+            if (status == TCL_BREAK) {
+                return 1;               /* This was caused by a "imgflush"
+                                         * command. Break out of the read loop
+                                         * and allow a new image to be
+                                         * rendered. */
+            } else { //if (status != TCL_OK) {
+                ret = 0;
+                if (handleError(interp, clientData, status, fdOut) < 0) {
+                    return -1;
+                }
+            }
+        }
+
+        tv.tv_sec = tv.tv_usec = 0L;    /* On successive reads, we break out
+                                         * if no data is available. */
+        FD_SET(inBufPtr->file(), &readFds);
+        tvPtr = &tv;
+    }
+
+    return ret;
+}
+
+/**
+ * \brief Send error message to client socket
+ */
+int
+VtkVis::handleError(Tcl_Interp *interp,
+                    ClientData clientData,
+                    int status, int fdOut)
+{
+    const char *string;
+    int nBytes;
+
+    if (status != TCL_OK) {
+        string = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
+        nBytes = strlen(string);
+        if (nBytes > 0) {
+            TRACE("status=%d errorInfo=(%s)", status, string);
+
+            std::ostringstream oss;
+            oss << "nv>viserror -type internal_error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string;
+            std::string ostr = oss.str();
+            nBytes = ostr.length();
+
+#ifdef USE_THREADS
+            queueResponse(clientData, ostr.c_str(), nBytes, Response::VOLATILE, Response::ERROR);
+#else
+            if (write(fdOut, ostr.c_str(), nBytes) < 0) {
+                ERROR("write failed: %s", strerror(errno));
+                return -1;
+            }
+#endif
+        }
+    }
+
+    std::string msg = getUserMessages();
+    nBytes = msg.length();
+    if (nBytes > 0) {
+        string = msg.c_str();
+        TRACE("userError=(%s)", string);
+
+        std::ostringstream oss;
+        oss << "nv>viserror -type error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string;
+        std::string ostr = oss.str();
+        nBytes = ostr.length();
+
+#ifdef USE_THREADS
+        queueResponse(clientData, ostr.c_str(), nBytes, Response::VOLATILE, Response::ERROR);
+#else
+        if (write(fdOut, ostr.c_str(), nBytes) < 0) {
+            ERROR("write failed: %s", strerror(errno));
+            return -1;
+        }
+#endif
+        clearUserMessages();
+    }
+
+    return 0;
+}
+
+/**
+ * \brief Create Tcl interpreter and add commands
+ *
+ * \return The initialized Tcl interpreter
+ */
+void
+VtkVis::initTcl(Tcl_Interp *interp, ClientData clientData)
+{
+    Tcl_MakeSafe(interp);
+    Tcl_CreateObjCommand(interp, "arc",            ArcCmd,            clientData, NULL);
+    Tcl_CreateObjCommand(interp, "arrow",          ArrowCmd,          clientData, NULL);
+    Tcl_CreateObjCommand(interp, "axis",           AxisCmd,           clientData, NULL);
+    Tcl_CreateObjCommand(interp, "box",            BoxCmd,            clientData, NULL);
+    Tcl_CreateObjCommand(interp, "camera",         CameraCmd,         clientData, NULL);
+    Tcl_CreateObjCommand(interp, "clientinfo",     ClientInfoCmd,     clientData, NULL);
+    Tcl_CreateObjCommand(interp, "colormap",       ColorMapCmd,       clientData, NULL);
+    Tcl_CreateObjCommand(interp, "cone",           ConeCmd,           clientData, NULL);
+    Tcl_CreateObjCommand(interp, "contour2d",      Contour2DCmd,      clientData, NULL);
+    Tcl_CreateObjCommand(interp, "contour3d",      Contour3DCmd,      clientData, NULL);
+    Tcl_CreateObjCommand(interp, "cutplane",       CutplaneCmd,       clientData, NULL);
+    Tcl_CreateObjCommand(interp, "cylinder",       CylinderCmd,       clientData, NULL);
+    Tcl_CreateObjCommand(interp, "dataset",        DataSetCmd,        clientData, NULL);
+    Tcl_CreateObjCommand(interp, "disk",           DiskCmd,           clientData, NULL);
+    Tcl_CreateObjCommand(interp, "glyphs",         GlyphsCmd,         clientData, NULL);
+    Tcl_CreateObjCommand(interp, "group",          GroupCmd,          clientData, NULL);
+    Tcl_CreateObjCommand(interp, "heightmap",      HeightMapCmd,      clientData, NULL);
+    Tcl_CreateObjCommand(interp, "image",          ImageCmd,          clientData, NULL);
+    Tcl_CreateObjCommand(interp, "imgcutplane",    ImageCutplaneCmd,  clientData, NULL);
+    Tcl_CreateObjCommand(interp, "imgflush",       ImageFlushCmd,     clientData, NULL);
+    Tcl_CreateObjCommand(interp, "legend",         LegendCmd,         clientData, NULL);
+    Tcl_CreateObjCommand(interp, "legend2",        LegendSimpleCmd,   clientData, NULL);
+    Tcl_CreateObjCommand(interp, "lic",            LICCmd,            clientData, NULL);
+    Tcl_CreateObjCommand(interp, "line",           LineCmd,           clientData, NULL);
+    Tcl_CreateObjCommand(interp, "molecule",       MoleculeCmd,       clientData, NULL);
+    Tcl_CreateObjCommand(interp, "outline",        OutlineCmd,        clientData, NULL);
+    Tcl_CreateObjCommand(interp, "parallelepiped", ParallelepipedCmd, clientData, NULL);
+    Tcl_CreateObjCommand(interp, "polydata",       PolyDataCmd,       clientData, NULL);
+    Tcl_CreateObjCommand(interp, "polygon",        PolygonCmd,        clientData, NULL);
+    Tcl_CreateObjCommand(interp, "pseudocolor",    PseudoColorCmd,    clientData, NULL);
+    Tcl_CreateObjCommand(interp, "renderer",       RendererCmd,       clientData, NULL);
+    Tcl_CreateObjCommand(interp, "screen",         ScreenCmd,         clientData, NULL);
+    Tcl_CreateObjCommand(interp, "sphere",         SphereCmd,         clientData, NULL);
+    Tcl_CreateObjCommand(interp, "streamlines",    StreamlinesCmd,    clientData, NULL);
+    Tcl_CreateObjCommand(interp, "text3d",         Text3DCmd,         clientData, NULL);
+    Tcl_CreateObjCommand(interp, "volume",         VolumeCmd,         clientData, NULL);
+    Tcl_CreateObjCommand(interp, "warp",           WarpCmd,           clientData, NULL);
+}
+
+/**
+ * \brief Delete Tcl commands and interpreter
+ */
+void VtkVis::exitTcl(Tcl_Interp *interp)
+{
+    Tcl_DeleteCommand(interp, "arc");
+    Tcl_DeleteCommand(interp, "arrow");
+    Tcl_DeleteCommand(interp, "axis");
+    Tcl_DeleteCommand(interp, "box");
+    Tcl_DeleteCommand(interp, "camera");
+    Tcl_DeleteCommand(interp, "clientinfo");
+    Tcl_DeleteCommand(interp, "colormap");
+    Tcl_DeleteCommand(interp, "cone");
+    Tcl_DeleteCommand(interp, "contour2d");
+    Tcl_DeleteCommand(interp, "contour3d");
+    Tcl_DeleteCommand(interp, "cutplane");
+    Tcl_DeleteCommand(interp, "cylinder");
+    Tcl_DeleteCommand(interp, "dataset");
+    Tcl_DeleteCommand(interp, "disk");
+    Tcl_DeleteCommand(interp, "glyphs");
+    Tcl_DeleteCommand(interp, "group");
+    Tcl_DeleteCommand(interp, "heightmap");
+    Tcl_DeleteCommand(interp, "image");
+    Tcl_DeleteCommand(interp, "imgcutplane");
+    Tcl_DeleteCommand(interp, "imgflush");
+    Tcl_DeleteCommand(interp, "legend");
+    Tcl_DeleteCommand(interp, "legend2");
+    Tcl_DeleteCommand(interp, "lic");
+    Tcl_DeleteCommand(interp, "line");
+    Tcl_DeleteCommand(interp, "molecule");
+    Tcl_DeleteCommand(interp, "outline");
+    Tcl_DeleteCommand(interp, "parallelepiped");
+    Tcl_DeleteCommand(interp, "polydata");
+    Tcl_DeleteCommand(interp, "polygon");
+    Tcl_DeleteCommand(interp, "pseudocolor");
+    Tcl_DeleteCommand(interp, "renderer");
+    Tcl_DeleteCommand(interp, "screen");
+    Tcl_DeleteCommand(interp, "sphere");
+    Tcl_DeleteCommand(interp, "streamlines");
+    Tcl_DeleteCommand(interp, "text3d");
+    Tcl_DeleteCommand(interp, "volume");
+    Tcl_DeleteCommand(interp, "warp");
+
+    Tcl_DeleteInterp(interp);
+}
Index: vtkvis/tags/1.8.4/RendererCmd.h
===================================================================
--- vtkvis/tags/1.8.4/RendererCmd.h	(revision 6184)
+++ vtkvis/tags/1.8.4/RendererCmd.h	(revision 6184)
@@ -0,0 +1,44 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_RENDERERCMD_H
+#define VTKVIS_RENDERERCMD_H
+
+#include <cstdio>
+#include <tcl.h>
+
+#include "ReadBuffer.h"
+#ifdef USE_THREADS
+#include "ResponseQueue.h"
+#endif
+
+namespace VtkVis {
+
+#ifdef USE_THREADS
+extern void queueResponse(ClientData clientData, const void *bytes, size_t len, 
+                          Response::AllocationType allocType,
+                          Response::ResponseType type = Response::DATA);
+#endif
+
+extern int processCommands(Tcl_Interp *interp,
+                           ClientData clientData,
+                           ReadBuffer *inBufPtr,
+                           int fdOut);
+
+extern int handleError(Tcl_Interp *interp,
+                       ClientData clientData,
+                       int status,
+                       int fdOut);
+
+extern void initTcl(Tcl_Interp *interp,
+                    ClientData clientData);
+
+extern void exitTcl(Tcl_Interp *interp);
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/RendererGraphicsObjs.cpp
===================================================================
--- vtkvis/tags/1.8.4/RendererGraphicsObjs.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/RendererGraphicsObjs.cpp	(revision 6184)
@@ -0,0 +1,4529 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cstring>
+#include <typeinfo>
+#include <vector>
+
+#include <vtkVersion.h>
+#include <vtkSmartPointer.h>
+#include <vtkDataSet.h>
+#include <vtkCharArray.h>
+#include <vtkDataSetReader.h>
+
+#include "RendererGraphicsObjs.h"
+#include "Renderer.h"
+#include "DataSet.h"
+#include "Box.h"
+#include "Contour2D.h"
+#include "Contour3D.h"
+#include "Cutplane.h"
+#include "Glyphs.h"
+#include "HeightMap.h"
+#include "LIC.h"
+#include "Line.h"
+#include "Molecule.h"
+#include "Parallelepiped.h"
+#include "PolyData.h"
+#include "PseudoColor.h"
+#include "Sphere.h"
+#include "Streamlines.h"
+#include "Volume.h"
+#include "ColorMap.h"
+#include "Trace.h"
+
+// Template specializations
+namespace VtkVis {
+
+template<>
+Renderer::ArcHashmap &
+Renderer::getGraphicsObjectHashmap<Arc>()
+{ return _arcs; }
+
+template<>
+Renderer::ArrowHashmap &
+Renderer::getGraphicsObjectHashmap<Arrow>()
+{ return _arrows; }
+
+template<>
+Renderer::BoxHashmap &
+Renderer::getGraphicsObjectHashmap<Box>()
+{ return _boxes; }
+
+template<>
+Renderer::ConeHashmap &
+Renderer::getGraphicsObjectHashmap<Cone>()
+{ return _cones; }
+
+template<>
+Renderer::Contour2DHashmap &
+Renderer::getGraphicsObjectHashmap<Contour2D>()
+{ return _contour2Ds; }
+
+template<>
+Renderer::Contour3DHashmap &
+Renderer::getGraphicsObjectHashmap<Contour3D>()
+{ return _contour3Ds; }
+
+template<>
+Renderer::CutplaneHashmap &
+Renderer::getGraphicsObjectHashmap<Cutplane>()
+{ return _cutplanes; }
+
+template<>
+Renderer::CylinderHashmap &
+Renderer::getGraphicsObjectHashmap<Cylinder>()
+{ return _cylinders; }
+
+template<>
+Renderer::DiskHashmap &
+Renderer::getGraphicsObjectHashmap<Disk>()
+{ return _disks; }
+
+template<>
+Renderer::GlyphsHashmap &
+Renderer::getGraphicsObjectHashmap<Glyphs>()
+{ return _glyphs; }
+
+template<>
+Renderer::GroupHashmap &
+Renderer::getGraphicsObjectHashmap<Group>()
+{ return _groups; }
+
+template<>
+Renderer::HeightMapHashmap &
+Renderer::getGraphicsObjectHashmap<HeightMap>()
+{ return _heightMaps; }
+
+template<>
+Renderer::ImageHashmap &
+Renderer::getGraphicsObjectHashmap<Image>()
+{ return _images; }
+
+template<>
+Renderer::ImageCutplaneHashmap &
+Renderer::getGraphicsObjectHashmap<ImageCutplane>()
+{ return _imageCutplanes; }
+
+template<>
+Renderer::LICHashmap &
+Renderer::getGraphicsObjectHashmap<LIC>()
+{ return _lics; }
+
+template<>
+Renderer::LineHashmap &
+Renderer::getGraphicsObjectHashmap<Line>()
+{ return _lines; }
+
+template<>
+Renderer::MoleculeHashmap &
+Renderer::getGraphicsObjectHashmap<Molecule>()
+{ return _molecules; }
+
+template<>
+Renderer::OutlineHashmap &
+Renderer::getGraphicsObjectHashmap<Outline>()
+{ return _outlines; }
+
+template<>
+Renderer::ParallelepipedHashmap &
+Renderer::getGraphicsObjectHashmap<Parallelepiped>()
+{ return _parallelepipeds; }
+
+template<>
+Renderer::PolyDataHashmap &
+Renderer::getGraphicsObjectHashmap<PolyData>()
+{ return _polyDatas; }
+
+template<>
+Renderer::PolygonHashmap &
+Renderer::getGraphicsObjectHashmap<Polygon>()
+{ return _polygons; }
+
+template<>
+Renderer::PseudoColorHashmap &
+Renderer::getGraphicsObjectHashmap<PseudoColor>()
+{ return _pseudoColors; }
+
+template<>
+Renderer::SphereHashmap &
+Renderer::getGraphicsObjectHashmap<Sphere>()
+{ return _spheres; }
+
+template<>
+Renderer::StreamlinesHashmap &
+Renderer::getGraphicsObjectHashmap<Streamlines>()
+{ return _streamlines; }
+
+template<>
+Renderer::Text3DHashmap &
+Renderer::getGraphicsObjectHashmap<Text3D>()
+{ return _text3Ds; }
+
+template<>
+Renderer::VolumeHashmap &
+Renderer::getGraphicsObjectHashmap<Volume>()
+{ return _volumes; }
+
+template<>
+Renderer::WarpHashmap &
+Renderer::getGraphicsObjectHashmap<Warp>()
+{ return _warps; }
+
+#if 0
+template Arc *Renderer::getGraphicsObject(const DataSetId&);
+template Arrow *Renderer::getGraphicsObject(const DataSetId&);
+template Box *Renderer::getGraphicsObject(const DataSetId&);
+template Cone *Renderer::getGraphicsObject(const DataSetId&);
+template Cylinder *Renderer::getGraphicsObject(const DataSetId&);
+template Disk *Renderer::getGraphicsObject(const DataSetId&);
+template Group *Renderer::getGraphicsObject(const DataSetId&);
+template Line *Renderer::getGraphicsObject(const DataSetId&);
+template Parallelepiped *Renderer::getGraphicsObject(const DataSetId&);
+template Polygon *Renderer::getGraphicsObject(const DataSetId&);
+template Sphere *Renderer::getGraphicsObject(const DataSetId&);
+template Text3D *Renderer::getGraphicsObject(const DataSetId&);
+#endif
+
+template <>
+void Renderer::deleteGraphicsObject<Group>(const DataSetId& id)
+{
+    GroupHashmap& hashmap = getGraphicsObjectHashmap<Group>();
+    GroupHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        ERROR("Group not found: %s", id.c_str());
+        return;
+    }
+
+    TRACE("Deleting Group: %s", id.c_str());
+
+    do {
+        Group *gobj = itr->second;
+        if (gobj->getProp())
+            _renderer->RemoveViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->RemoveViewProp(gobj->getOverlayProp());
+
+        std::vector<GraphicsObject *> children;
+        gobj->getChildren(children);
+
+        // Un-grouping children
+        for (std::vector<GraphicsObject *>::iterator citr = children.begin();
+             citr != children.end(); ++citr) {
+            if ((*citr)->getProp())
+                _renderer->AddViewProp((*citr)->getProp());
+            if ((*citr)->getOverlayProp())
+                _renderer->AddViewProp((*citr)->getOverlayProp());
+        }
+
+        delete gobj;
+
+        itr = hashmap.erase(itr);
+    } while (doAll && itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+}
+
+using namespace VtkVis;
+
+GraphicsObject *
+Renderer::getGenericGraphicsObject(const DataSetId& id)
+{
+    GraphicsObject *gobj = NULL;
+
+    if ((gobj = getGraphicsObject<Arc>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Arrow>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Box>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Cone>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Cylinder>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Disk>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Line>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Parallelepiped>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Polygon>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Sphere>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Text3D>(id)) != NULL) {
+        return gobj;
+    }
+    //
+    if ((gobj = getGraphicsObject<Contour2D>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Contour3D>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Cutplane>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Glyphs>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<HeightMap>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Image>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<ImageCutplane>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<LIC>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Molecule>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Outline>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<PolyData>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<PseudoColor>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Streamlines>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Volume>(id)) != NULL) {
+        return gobj;
+    }
+    if ((gobj = getGraphicsObject<Warp>(id)) != NULL) {
+        return gobj;
+    }
+    //
+    if ((gobj = getGraphicsObject<Group>(id)) != NULL) {
+        return gobj;
+    }
+
+    return NULL;
+}
+
+/**
+ * \brief Create a new Arc and associate it with an ID
+ */
+bool Renderer::addArc(const DataSetId& id,
+                      double center[3],
+                      double pt1[3],
+                      double normal[3],
+                      double angle)
+{
+    Arc *gobj;
+    if ((gobj = getGraphicsObject<Arc>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Arc>(id);
+    }
+
+    gobj = new Arc();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setCenter(center);
+    gobj->setStartPoint(pt1);
+    gobj->setNormal(normal);
+    gobj->setAngle(angle);
+
+    getGraphicsObjectHashmap<Arc>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set Arc resolution
+ */
+void Renderer::setArcResolution(const DataSetId& id, int res)
+{
+    ArcHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _arcs.begin();
+        if (itr == _arcs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _arcs.find(id);
+    }
+    if (itr == _arcs.end()) {
+        ERROR("Arc not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setResolution(res);
+    } while (doAll && ++itr != _arcs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Arrow and associate it with an ID
+ */
+bool Renderer::addArrow(const DataSetId& id, double tipRadius,
+                        double shaftRadius, double tipLength,
+                        bool flipNormals)
+{
+    Arrow *gobj;
+    if ((gobj = getGraphicsObject<Arrow>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Arrow>(id);
+    }
+
+    gobj = new Arrow();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setRadii(tipRadius, shaftRadius);
+    gobj->setTipLength(tipLength);
+    if (flipNormals)
+        gobj->flipNormals(flipNormals);
+
+    getGraphicsObjectHashmap<Arrow>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set Arrow resolution
+ */
+void Renderer::setArrowResolution(const DataSetId& id, int tipRes, int shaftRes)
+{
+    ArrowHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _arrows.begin();
+        if (itr == _arrows.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _arrows.find(id);
+    }
+    if (itr == _arrows.end()) {
+        ERROR("Arrow not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setResolution(tipRes, shaftRes);
+    } while (doAll && ++itr != _arrows.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Box and associate it with an ID
+ */
+bool Renderer::addBox(const DataSetId& id,
+                      double xLen, double yLen, double zLen,
+                      bool flipNormals)
+{
+    Box *gobj;
+    if ((gobj = getGraphicsObject<Box>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Box>(id);
+    }
+
+    gobj = new Box();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setSize(xLen, yLen, zLen);
+    if (flipNormals)
+        gobj->flipNormals(flipNormals);
+
+    getGraphicsObjectHashmap<Box>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Create a new Cone and associate it with an ID
+ */
+bool Renderer::addCone(const DataSetId& id, double radius, double height,
+                       bool cap, bool flipNormals)
+{
+    Cone *gobj;
+    if ((gobj = getGraphicsObject<Cone>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Cone>(id);
+    }
+
+    gobj = new Cone();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setRadius(radius);
+    gobj->setHeight(height);
+    gobj->setCapping(cap);
+    if (flipNormals)
+        gobj->flipNormals(flipNormals);
+
+    getGraphicsObjectHashmap<Cone>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set Cone resolution
+ */
+void Renderer::setConeResolution(const DataSetId& id, int res)
+{
+    ConeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cones.begin();
+        if (itr == _cones.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cones.find(id);
+    }
+    if (itr == _cones.end()) {
+        ERROR("Cone not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setResolution(res);
+    } while (doAll && ++itr != _cones.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Contour2D and associate it with the named DataSet
+ */
+bool Renderer::addContour2D(const DataSetId& id, int numContours)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        if (getGraphicsObject<Contour2D>(dsID)) {
+            WARN("Replacing existing Contour2D %s", dsID.c_str());
+            deleteGraphicsObject<Contour2D>(dsID);
+        }
+
+        Contour2D *contour = new Contour2D(numContours);
+ 
+        contour->setDataSet(ds, this);
+
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour2Ds[dsID] = contour;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Create a new Contour2D and associate it with the named DataSet
+ */
+bool Renderer::addContour2D(const DataSetId& id, const std::vector<double>& contours)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        if (getGraphicsObject<Contour2D>(dsID)) {
+            WARN("Replacing existing Contour2D %s", dsID.c_str());
+            deleteGraphicsObject<Contour2D>(dsID);
+        }
+
+        Contour2D *contour = new Contour2D(contours);
+
+        contour->setDataSet(ds, this);
+
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour2Ds[dsID] = contour;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set the number of equally spaced contour isolines for the given DataSet
+ */
+void Renderer::setContour2DNumContours(const DataSetId& id, int numContours)
+{
+    Contour2DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour2Ds.begin();
+        doAll = true;
+    } else {
+        itr = _contour2Ds.find(id);
+    }
+    if (itr == _contour2Ds.end()) {
+        ERROR("Contour2D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setNumContours(numContours);
+    } while (doAll && ++itr != _contour2Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the number of equally spaced isosurfaces for the given DataSet
+ */
+void Renderer::setContour2DContourField(const DataSetId& id, const char *fieldName)
+{
+    Contour2DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour2Ds.begin();
+        doAll = true;
+    } else {
+        itr = _contour2Ds.find(id);
+    }
+    if (itr == _contour2Ds.end()) {
+        ERROR("Contour2D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourField(fieldName);
+     } while (doAll && ++itr != _contour2Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set a list of isovalues for the given DataSet
+ */
+void Renderer::setContour2DContourList(const DataSetId& id, const std::vector<double>& contours)
+{
+    Contour2DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour2Ds.begin();
+        doAll = true;
+    } else {
+        itr = _contour2Ds.find(id);
+    }
+    if (itr == _contour2Ds.end()) {
+        ERROR("Contour2D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourList(contours);
+    } while (doAll && ++itr != _contour2Ds.end());
+
+    sceneBoundsChanged();
+     _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setContour2DColorMode(const DataSetId& id,
+                                     Contour2D::ColorMode mode,
+                                     DataSet::DataAttributeType type,
+                                     const char *name, double range[2])
+{
+    Contour2DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour2Ds.begin();
+        if (itr == _contour2Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _contour2Ds.find(id);
+    }
+    if (itr == _contour2Ds.end()) {
+        ERROR("Contour2D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _contour2Ds.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setContour2DColorMode(const DataSetId& id,
+                                     Contour2D::ColorMode mode,
+                                     const char *name, double range[2])
+{
+    Contour2DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour2Ds.begin();
+        if (itr == _contour2Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _contour2Ds.find(id);
+    }
+    if (itr == _contour2Ds.end()) {
+        ERROR("Contour2D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _contour2Ds.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Contour3D and associate it with the named DataSet
+ */
+bool Renderer::addContour3D(const DataSetId& id, int numContours)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        if (getGraphicsObject<Contour3D>(dsID)) {
+            WARN("Replacing existing Contour3D %s", dsID.c_str());
+            deleteGraphicsObject<Contour3D>(dsID);
+        }
+
+        Contour3D *contour = new Contour3D(numContours);
+
+        contour->setDataSet(ds, this);
+
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour3Ds[dsID] = contour;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Create a new Contour3D and associate it with the named DataSet
+ */
+bool Renderer::addContour3D(const DataSetId& id,const std::vector<double>& contours)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        if (getGraphicsObject<Contour3D>(dsID)) {
+            WARN("Replacing existing Contour3D %s", dsID.c_str());
+            deleteGraphicsObject<Contour3D>(dsID);
+        }
+
+        Contour3D *contour = new Contour3D(contours);
+
+        contour->setDataSet(ds, this);
+
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour3Ds[dsID] = contour;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set the number of equally spaced isosurfaces for the given DataSet
+ */
+void Renderer::setContour3DContourField(const DataSetId& id, const char *fieldName)
+{
+    Contour3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour3Ds.begin();
+        doAll = true;
+    } else {
+        itr = _contour3Ds.find(id);
+    }
+    if (itr == _contour3Ds.end()) {
+        ERROR("Contour3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourField(fieldName);
+     } while (doAll && ++itr != _contour3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the number of equally spaced isosurfaces for the given DataSet
+ */
+void Renderer::setContour3DNumContours(const DataSetId& id, int numContours)
+{
+    Contour3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour3Ds.begin();
+        doAll = true;
+    } else {
+        itr = _contour3Ds.find(id);
+    }
+    if (itr == _contour3Ds.end()) {
+        ERROR("Contour3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setNumContours(numContours);
+     } while (doAll && ++itr != _contour3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set a list of isovalues for the given DataSet
+ */
+void Renderer::setContour3DContourList(const DataSetId& id, const std::vector<double>& contours)
+{
+    Contour3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour3Ds.begin();
+        doAll = true;
+    } else {
+        itr = _contour3Ds.find(id);
+    }
+    if (itr == _contour3Ds.end()) {
+        ERROR("Contour3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourList(contours);
+    } while (doAll && ++itr != _contour3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setContour3DColorMode(const DataSetId& id,
+                                     Contour3D::ColorMode mode,
+                                     DataSet::DataAttributeType type,
+                                     const char *name, double range[2])
+{
+    Contour3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour3Ds.begin();
+        if (itr == _contour3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _contour3Ds.find(id);
+    }
+    if (itr == _contour3Ds.end()) {
+        ERROR("Contour3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _contour3Ds.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setContour3DColorMode(const DataSetId& id,
+                                     Contour3D::ColorMode mode,
+                                     const char *name, double range[2])
+{
+    Contour3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _contour3Ds.begin();
+        if (itr == _contour3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _contour3Ds.find(id);
+    }
+    if (itr == _contour3Ds.end()) {
+        ERROR("Contour3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _contour3Ds.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the visibility of cutplane outlines
+ */
+void Renderer::setCutplaneOutlineVisibility(const DataSetId& id, bool state)
+{
+    CutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cutplanes.begin();
+        if (itr == _cutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cutplanes.find(id);
+    }
+
+    if (itr == _cutplanes.end()) {
+        ERROR("Cutplane not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOutlineVisibility(state);
+     } while (doAll && ++itr != _cutplanes.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the visibility of slices in one of the three axes
+ */
+void Renderer::setCutplaneSliceVisibility(const DataSetId& id, Axis axis, bool state)
+{
+    CutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cutplanes.begin();
+        if (itr == _cutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cutplanes.find(id);
+    }
+
+    if (itr == _cutplanes.end()) {
+        ERROR("Cutplane not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSliceVisibility(axis, state);
+     } while (doAll && ++itr != _cutplanes.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the point cloud render style for the specified DataSet
+ */
+void Renderer::setCutplaneCloudStyle(const DataSetId& id,
+                                     Cutplane::CloudStyle style)
+{
+    CutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cutplanes.begin();
+        if (itr == _cutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cutplanes.find(id);
+    }
+    if (itr == _cutplanes.end()) {
+        ERROR("Cutplane not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCloudStyle(style);
+    } while (doAll && ++itr != _cutplanes.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setCutplaneColorMode(const DataSetId& id,
+                                    Cutplane::ColorMode mode,
+                                    DataSet::DataAttributeType type,
+                                    const char *name, double range[2])
+{
+    CutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cutplanes.begin();
+        if (itr == _cutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cutplanes.find(id);
+    }
+    if (itr == _cutplanes.end()) {
+        ERROR("Cutplane not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _cutplanes.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setCutplaneColorMode(const DataSetId& id,
+                                    Cutplane::ColorMode mode,
+                                    const char *name, double range[2])
+{
+    CutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cutplanes.begin();
+        if (itr == _cutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cutplanes.find(id);
+    }
+    if (itr == _cutplanes.end()) {
+        ERROR("Cutplane not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _cutplanes.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Cylinder and associate it with an ID
+ */
+bool Renderer::addCylinder(const DataSetId& id, double radius, double height,
+                           bool cap, bool flipNormals)
+{
+    Cylinder *gobj;
+    if ((gobj = getGraphicsObject<Cylinder>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Cylinder>(id);
+    }
+
+    gobj = new Cylinder();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setRadius(radius);
+    gobj->setHeight(height);
+    gobj->setCapping(cap);
+    if (flipNormals)
+        gobj->flipNormals(flipNormals);
+
+    getGraphicsObjectHashmap<Cylinder>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set Cylinder capping
+ */
+void Renderer::setCylinderCapping(const DataSetId& id, bool state)
+{
+    CylinderHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cylinders.begin();
+        if (itr == _cylinders.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cylinders.find(id);
+    }
+    if (itr == _cylinders.end()) {
+        ERROR("Cylinder not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCapping(state);
+    } while (doAll && ++itr != _cylinders.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set Cylinder resolution
+ */
+void Renderer::setCylinderResolution(const DataSetId& id, int res)
+{
+    CylinderHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _cylinders.begin();
+        if (itr == _cylinders.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _cylinders.find(id);
+    }
+    if (itr == _cylinders.end()) {
+        ERROR("Cylinder not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setResolution(res);
+    } while (doAll && ++itr != _cylinders.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Disk and associate it with an ID
+ */
+bool Renderer::addDisk(const DataSetId& id,
+                       double innerRadius,
+                       double outerRadius,
+                       bool flipNormals)
+{
+    Disk *gobj;
+    if ((gobj = getGraphicsObject<Disk>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Disk>(id);
+    }
+
+    gobj = new Disk();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setRadii(innerRadius, outerRadius);
+    if (flipNormals)
+        gobj->flipNormals(flipNormals);
+
+    getGraphicsObjectHashmap<Disk>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set Disk resolution
+ */
+void Renderer::setDiskResolution(const DataSetId& id, int resRadial, int resCircum)
+{
+    DiskHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _disks.begin();
+        if (itr == _disks.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _disks.find(id);
+    }
+    if (itr == _disks.end()) {
+        ERROR("Disk not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setResolution(resRadial, resCircum);
+    } while (doAll && ++itr != _disks.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Glyphs and associate it with the named DataSet
+ */
+bool Renderer::addGlyphs(const DataSetId& id, Glyphs::GlyphShape shape)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        if (getGraphicsObject<Glyphs>(dsID)) {
+            WARN("Replacing existing Glyphs %s", dsID.c_str());
+            deleteGraphicsObject<Glyphs>(dsID);
+        }
+
+        Glyphs *glyphs = new Glyphs(shape);
+
+        glyphs->setDataSet(ds, this);
+
+        if (glyphs->getProp() == NULL) {
+            delete glyphs;
+            return false;
+        } else {
+            _renderer->AddViewProp(glyphs->getProp());
+        }
+
+        _glyphs[dsID] = glyphs;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setGlyphsColorMode(const DataSetId& id,
+                                  Glyphs::ColorMode mode,
+                                  const char *name, double range[2])
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _glyphs.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Controls the array used to scale glyphs for the given DataSet
+ */
+void Renderer::setGlyphsScalingMode(const DataSetId& id,
+                                    Glyphs::ScalingMode mode,
+                                    const char *name, double range[2])
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setScalingMode(mode, name, range);
+    } while (doAll && ++itr != _glyphs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Limit the number of glyphs displayed
+ *
+ * The choice of glyphs to display can be based on sampling every
+ * n-th point (ratio) or by random sample
+ *
+ * \param id DataSet ID
+ * \param max Maximum number of glyphs to display, negative means display all
+ * \param random Flag to enable/disable random sampling
+ * \param offset If random is false, this controls the first sample point
+ * \param ratio If random is false, this ratio controls every n-th point sampling
+ */
+void Renderer::setGlyphsMaximumNumberOfGlyphs(const DataSetId& id, int max,
+                                              bool random, int offset, int ratio)
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setMaximumNumberOfGlyphs(max, random, offset, ratio);
+    } while (doAll && ++itr != _glyphs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Controls if field data range is normalized to [0,1] before
+ * applying scale factor for the given DataSet
+ */
+void Renderer::setGlyphsNormalizeScale(const DataSetId& id, bool normalize)
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setNormalizeScale(normalize);
+    } while (doAll && ++itr != _glyphs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Controls if glyphs are oriented from a vector field for the 
+ * given DataSet
+ */
+void Renderer::setGlyphsOrientMode(const DataSetId& id, bool state,
+                                   const char *name)
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOrientMode(state, name);
+    } while (doAll && ++itr != _glyphs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set glyph shape resolution
+ */
+void Renderer::setGlyphsQuality(const DataSetId& id, double quality)
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setQuality(quality);
+    } while (doAll && ++itr != _glyphs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the shape of Glyphs for the given DataSet
+ */
+void Renderer::setGlyphsShape(const DataSetId& id, Glyphs::GlyphShape shape)
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setGlyphShape(shape);
+    } while (doAll && ++itr != _glyphs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the glyph scaling factor for the given DataSet
+ */
+void Renderer::setGlyphsScaleFactor(const DataSetId& id, double scale)
+{
+    GlyphsHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _glyphs.begin();
+        if (itr == _glyphs.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _glyphs.find(id);
+    }
+    if (itr == _glyphs.end()) {
+        ERROR("Glyphs not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setScaleFactor(scale);
+    } while (doAll && ++itr != _glyphs.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+bool Renderer::addGroup(const DataSetId& id, const std::vector<Group::NodeId>& nodeList)
+{
+    if (id.compare("all") == 0) {
+        addChildrenToGroup(id, nodeList);
+        return true;
+    }
+
+    Group *gobj;
+    if ((gobj = getGraphicsObject<Group>(id)) != NULL) {
+        // Group exists, so add nodes to it
+        addChildrenToGroup(id, nodeList);
+        return true;
+    }
+
+    gobj = new Group();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    for (std::vector<Group::NodeId>::const_iterator itr = nodeList.begin();
+         itr != nodeList.end(); ++itr) {
+        GraphicsObject *node = getGenericGraphicsObject(*itr);
+        if (node != NULL) {
+            if (node->getProp())
+                _renderer->RemoveViewProp(node->getProp());
+            if (node->getOverlayProp())
+                _renderer->RemoveViewProp(node->getOverlayProp());
+            gobj->addChild(*itr, node);
+        } else {
+            ERROR("Can't find node: %s", itr->c_str());
+        }
+    }
+
+    getGraphicsObjectHashmap<Group>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true; 
+}
+
+void Renderer::addChildrenToGroup(const DataSetId& id, const std::vector<Group::NodeId>& nodeList)
+{
+    GroupHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _groups.begin();
+        if (itr == _groups.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _groups.find(id);
+    }
+
+    if (itr == _groups.end()) {
+        ERROR("Group not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        for (std::vector<Group::NodeId>::const_iterator citr = nodeList.begin();
+             citr != nodeList.end(); ++citr) {
+            GraphicsObject *node = getGenericGraphicsObject(*citr);
+            if (node != NULL) {
+                if (node->getProp())
+                    _renderer->RemoveViewProp(node->getProp());
+                if (node->getOverlayProp())
+                    _renderer->RemoveViewProp(node->getOverlayProp());
+                itr->second->addChild(*citr, node);
+            } else {
+                ERROR("Can't find node: %s", citr->c_str());
+            }
+        }
+     } while (doAll && ++itr != _groups.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::removeChildrenFromGroup(const DataSetId& id, const std::vector<Group::NodeId>& nodeList)
+{
+    GroupHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _groups.begin();
+        if (itr == _groups.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _groups.find(id);
+    }
+
+    if (itr == _groups.end()) {
+        ERROR("Group not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        for (std::vector<Group::NodeId>::const_iterator citr = nodeList.begin();
+             citr != nodeList.end(); ++citr) {
+            GraphicsObject *node = getGenericGraphicsObject(*citr);
+            if (node != NULL) {
+                if (node->getProp())
+                    _renderer->AddViewProp(node->getProp());
+                if (node->getOverlayProp())
+                    _renderer->AddViewProp(node->getOverlayProp());
+                assert(node == itr->second->removeChild(*citr));
+            } else {
+                ERROR("Can't find node: %s", citr->c_str());
+            }
+        }
+     } while (doAll && ++itr != _groups.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new HeightMap and associate it with the named DataSet
+ */
+bool Renderer::addHeightMap(const DataSetId& id, int numContours, double heightScale)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        if (getGraphicsObject<HeightMap>(dsID)) {
+            WARN("Replacing existing HeightMap %s", dsID.c_str());
+            deleteGraphicsObject<HeightMap>(dsID);
+        }
+
+        HeightMap *hmap = new HeightMap(numContours, heightScale);
+
+        hmap->setDataSet(ds, this);
+
+        if (hmap->getProp() == NULL) {
+            delete hmap;
+            return false;
+        } else {
+            _renderer->AddViewProp(hmap->getProp());
+        }
+
+        _heightMaps[dsID] = hmap;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Create a new HeightMap and associate it with the named DataSet
+ */
+bool Renderer::addHeightMap(const DataSetId& id, const std::vector<double>& contours, double heightScale)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        if (getGraphicsObject<HeightMap>(dsID)) {
+            WARN("Replacing existing HeightMap %s", dsID.c_str());
+            deleteGraphicsObject<HeightMap>(dsID);
+        }
+
+        HeightMap *hmap = new HeightMap(contours, heightScale);
+
+        hmap->setDataSet(ds, this);
+
+        if (hmap->getProp() == NULL) {
+            delete hmap;
+            return false;
+        } else {
+            _renderer->AddViewProp(hmap->getProp());
+        }
+
+        _heightMaps[dsID] = hmap;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set amount to scale scalar values when creating elevations
+ * in the height map
+ */
+void Renderer::setHeightMapHeightScale(const DataSetId& id, double scale)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setHeightScale(scale);
+     } while (doAll && ++itr != _heightMaps.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the point cloud render style for the specified DataSet
+ */
+void Renderer::setHeightMapCloudStyle(const DataSetId& id,
+                                      HeightMap::CloudStyle style)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCloudStyle(style);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the number of equally spaced contour isolines for the given DataSet
+ */
+void Renderer::setHeightMapNumContours(const DataSetId& id, int numContours)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setNumContours(numContours);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set a list of height map contour isovalues for the given DataSet
+ */
+void Renderer::setHeightMapContourList(const DataSetId& id, const std::vector<double>& contours)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourList(contours);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    sceneBoundsChanged();
+     _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off rendering height map contour lines for the given DataSet
+ */
+void Renderer::setHeightMapContourLineVisibility(const DataSetId& id, bool state)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourLineVisibility(state);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off rendering height map colormap surface for the given DataSet
+ */
+void Renderer::setHeightMapContourSurfaceVisibility(const DataSetId& id, bool state)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourSurfaceVisibility(state);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the RGB height map isoline color for the specified DataSet
+ */
+void Renderer::setHeightMapContourEdgeColor(const DataSetId& id, float color[3])
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourEdgeColor(color);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the height map isoline width for the specified DataSet (may be a no-op)
+ *
+ * If the OpenGL implementation/hardware does not support wide lines, 
+ * this function may not have an effect.
+ */
+void Renderer::setHeightMapContourEdgeWidth(const DataSetId& id, float edgeWidth)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourEdgeWidth(edgeWidth);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Toggle colormapping of contour lines
+ */
+void Renderer::setHeightMapContourLineColorMapEnabled(const DataSetId& id, bool mode)
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setContourLineColorMapEnabled(mode);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    _needsRedraw = true;    
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setHeightMapColorMode(const DataSetId& id,
+                                     HeightMap::ColorMode mode,
+                                     DataSet::DataAttributeType type,
+                                     const char *name, double range[2])
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setHeightMapColorMode(const DataSetId& id,
+                                     HeightMap::ColorMode mode,
+                                     const char *name, double range[2])
+{
+    HeightMapHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _heightMaps.begin();
+        if (itr == _heightMaps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _heightMaps.find(id);
+    }
+    if (itr == _heightMaps.end()) {
+        ERROR("HeightMap not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageBackground(const DataSetId& id, bool state)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBackground(state);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageBacking(const DataSetId& id, bool state)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBacking(state);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageBorder(const DataSetId& id, bool state)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBorder(state);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageExtents(const DataSetId& id, int extents[6])
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setExtents(extents);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageLevel(const DataSetId& id, double level)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setLevel(level);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageWindow(const DataSetId& id, double window)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setWindow(window);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageSliceInterp(const DataSetId& id, bool state)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setJumpToNearestSlice(!state);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageSlicePlane(const DataSetId& id, double normal[3], double origin[3])
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSlicePlane(normal, origin);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageSliceFollowsCamera(const DataSetId& id, bool state)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSliceFollowsCamera(state);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageZSlice(const DataSetId& id, int z)
+{
+    ImageHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _images.begin();
+        if (itr == _images.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _images.find(id);
+    }
+    if (itr == _images.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setZSlice(z);
+    } while (doAll && ++itr != _images.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the visibility of cutplane outlines
+ */
+void Renderer::setImageCutplaneOutlineVisibility(const DataSetId& id, bool state)
+{
+    ImageCutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _imageCutplanes.begin();
+        if (itr == _imageCutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _imageCutplanes.find(id);
+    }
+
+    if (itr == _imageCutplanes.end()) {
+        ERROR("ImageCutplane not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOutlineVisibility(state);
+     } while (doAll && ++itr != _imageCutplanes.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the visibility of slices in one of the three axes
+ */
+void Renderer::setImageCutplaneSliceVisibility(const DataSetId& id, Axis axis, bool state)
+{
+    ImageCutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _imageCutplanes.begin();
+        if (itr == _imageCutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _imageCutplanes.find(id);
+    }
+
+    if (itr == _imageCutplanes.end()) {
+        ERROR("ImageCutplane not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSliceVisibility(axis, state);
+     } while (doAll && ++itr != _imageCutplanes.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::setImageCutplaneLevel(const DataSetId& id, double level)
+{
+    ImageCutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _imageCutplanes.begin();
+        if (itr == _imageCutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _imageCutplanes.find(id);
+    }
+    if (itr == _imageCutplanes.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setLevel(level);
+    } while (doAll && ++itr != _imageCutplanes.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setImageCutplaneWindow(const DataSetId& id, double window)
+{
+    ImageCutplaneHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _imageCutplanes.begin();
+        if (itr == _imageCutplanes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _imageCutplanes.find(id);
+    }
+    if (itr == _imageCutplanes.end()) {
+        ERROR("Image not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setWindow(window);
+    } while (doAll && ++itr != _imageCutplanes.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Line and associate it with an ID
+ */
+bool Renderer::addLine(const DataSetId& id, double pt1[3], double pt2[3])
+{
+    Line *gobj;
+    if ((gobj = getGraphicsObject<Line>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Line>(id);
+    }
+
+    gobj = new Line();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setEndPoints(pt1, pt2);
+
+    getGraphicsObjectHashmap<Line>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Create a new Line and associate it with an ID
+ */
+bool Renderer::addLine(const DataSetId& id, std::vector<double> points)
+{
+    Line *gobj;
+    if ((gobj = getGraphicsObject<Line>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Line>(id);
+    }
+
+    gobj = new Line();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setPoints(points);
+
+    getGraphicsObjectHashmap<Line>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set atom sphere resolution
+ */
+void Renderer::setMoleculeAtomQuality(const DataSetId& id, double quality)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAtomQuality(quality);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set bond cylinder resolution
+ */
+void Renderer::setMoleculeBondQuality(const DataSetId& id, double quality)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBondQuality(quality);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set radius scale factor for atoms
+ */
+void Renderer::setMoleculeAtomRadiusScale(const DataSetId& id, double scale)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAtomRadiusScale(scale);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set radius standard for scaling atoms
+ */
+void Renderer::setMoleculeAtomScaling(const DataSetId& id, Molecule::AtomScaling scaling)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAtomScaling(scaling);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off rendering of the Molecule atoms for the given DataSet
+ */
+void Renderer::setMoleculeAtomVisibility(const DataSetId& id, bool state)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAtomVisibility(state);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the field used to label atoms for the given DataSet
+ */
+void Renderer::setMoleculeAtomLabelField(const DataSetId& id, const char *fieldName)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAtomLabelField(fieldName);
+    } while (doAll && ++itr != _molecules.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off rendering of the Molecule atom labels for the given DataSet
+ */
+void Renderer::setMoleculeAtomLabelVisibility(const DataSetId& id, bool state)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAtomLabelVisibility(state);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set radius scale factor for atoms
+ */
+void Renderer::setMoleculeBondRadiusScale(const DataSetId& id, double scale)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBondRadiusScale(scale);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off rendering of the Molecule bonds for the given DataSet
+ */
+void Renderer::setMoleculeBondVisibility(const DataSetId& id, bool state)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBondVisibility(state);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set render style of the Molecule bonds for the given DataSet
+ */
+void Renderer::setMoleculeBondStyle(const DataSetId& id, Molecule::BondStyle style)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBondStyle(style);
+    } while (doAll && ++itr != _molecules.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set coloring mode of the Molecule bonds for the given DataSet
+ */
+void Renderer::setMoleculeBondColorMode(const DataSetId& id, Molecule::BondColorMode mode)
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBondColorMode(mode);
+    } while (doAll && ++itr != _molecules.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set constant color of the Molecule bonds for the given DataSet
+ */
+void Renderer::setMoleculeBondColor(const DataSetId& id, float color[3])
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBondColor(color);
+    } while (doAll && ++itr != _molecules.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setMoleculeColorMode(const DataSetId& id,
+                                    Molecule::ColorMode mode,
+                                    DataSet::DataAttributeType type,
+                                    const char *name, double range[2])
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _molecules.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setMoleculeColorMode(const DataSetId& id,
+                                    Molecule::ColorMode mode,
+                                    const char *name, double range[2])
+{
+    MoleculeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _molecules.begin();
+        if (itr == _molecules.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _molecules.find(id);
+    }
+    if (itr == _molecules.end()) {
+        ERROR("Molecule not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _molecules.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Parallelepiped and associate it with an ID
+ */
+bool Renderer::addParallelepiped(const DataSetId& id,
+                                 double vec1[3], double vec2[3], double vec3[3],
+                                 bool flipNormals)
+{
+    Parallelepiped *gobj;
+    if ((gobj = getGraphicsObject<Parallelepiped>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Parallelepiped>(id);
+    }
+
+    gobj = new Parallelepiped();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setVectors(vec1, vec2, vec3);
+    if (flipNormals)
+        gobj->flipNormals(flipNormals);
+
+    getGraphicsObjectHashmap<Parallelepiped>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Create a new n-sided regular Polygon and associate it with an ID
+ */
+bool Renderer::addPolygon(const DataSetId& id, int numSides,
+                          double center[3], double normal[3], double radius)
+{
+    Polygon *gobj;
+    if ((gobj = getGraphicsObject<Polygon>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Polygon>(id);
+    }
+
+    gobj = new Polygon();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setNumberOfSides(numSides);
+    gobj->setCenter(center);
+    gobj->setNormal(normal);
+    gobj->setRadius(radius);
+
+    getGraphicsObjectHashmap<Polygon>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set the  point cloud render style for the specified DataSet
+ */
+void Renderer::setPolyDataCloudStyle(const DataSetId& id,
+                                     PolyData::CloudStyle style)
+{
+    PolyDataHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _polyDatas.begin();
+        if (itr == _polyDatas.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _polyDatas.find(id);
+    }
+    if (itr == _polyDatas.end()) {
+        ERROR("PolyData not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCloudStyle(style);
+    } while (doAll && ++itr != _polyDatas.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setPolyDataColorMode(const DataSetId& id,
+                                    PolyData::ColorMode mode,
+                                    DataSet::DataAttributeType type,
+                                    const char *name, double range[2])
+{
+    PolyDataHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _polyDatas.begin();
+        if (itr == _polyDatas.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _polyDatas.find(id);
+    }
+    if (itr == _polyDatas.end()) {
+        ERROR("PolyData not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _polyDatas.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setPolyDataColorMode(const DataSetId& id,
+                                    PolyData::ColorMode mode,
+                                    const char *name, double range[2])
+{
+    PolyDataHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _polyDatas.begin();
+        if (itr == _polyDatas.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _polyDatas.find(id);
+    }
+    if (itr == _polyDatas.end()) {
+        ERROR("PolyData not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _polyDatas.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the  point cloud render style for the specified DataSet
+ */
+void Renderer::setPseudoColorCloudStyle(const DataSetId& id,
+                                        PseudoColor::CloudStyle style)
+{
+    PseudoColorHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _pseudoColors.begin();
+        if (itr == _pseudoColors.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _pseudoColors.find(id);
+    }
+    if (itr == _pseudoColors.end()) {
+        ERROR("PseudoColor not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCloudStyle(style);
+    } while (doAll && ++itr != _pseudoColors.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setPseudoColorColorMode(const DataSetId& id,
+                                       PseudoColor::ColorMode mode,
+                                       DataSet::DataAttributeType type,
+                                       const char *name, double range[2])
+{
+    PseudoColorHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _pseudoColors.begin();
+        if (itr == _pseudoColors.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _pseudoColors.find(id);
+    }
+    if (itr == _pseudoColors.end()) {
+        ERROR("PseudoColor not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _pseudoColors.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setPseudoColorColorMode(const DataSetId& id,
+                                       PseudoColor::ColorMode mode,
+                                       const char *name, double range[2])
+{
+    PseudoColorHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _pseudoColors.begin();
+        if (itr == _pseudoColors.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _pseudoColors.find(id);
+    }
+    if (itr == _pseudoColors.end()) {
+        ERROR("PseudoColor not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _pseudoColors.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Create a new Sphere and associate it with an ID
+ */
+bool Renderer::addSphere(const DataSetId& id, double center[3], double radius, bool flipNormals)
+{
+    Sphere *gobj;
+    if ((gobj = getGraphicsObject<Sphere>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Sphere>(id);
+    }
+
+    gobj = new Sphere();
+ 
+    gobj->setDataSet(NULL, this);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    gobj->setCenter(center);
+    gobj->setRadius(radius);
+    if (flipNormals)
+        gobj->flipNormals(flipNormals);
+
+    getGraphicsObjectHashmap<Sphere>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Create a new Text3D label and associate it with an ID
+ */
+bool Renderer::addText3D(const DataSetId& id, const char *string,
+                         const char *fontFamily, int fontSize,
+                         bool bold, bool italic, bool shadow)
+{
+    Text3D *gobj;
+    if ((gobj = getGraphicsObject<Text3D>(id)) != NULL) {
+        WARN("Replacing existing %s %s", gobj->getClassName(), id.c_str());
+        deleteGraphicsObject<Text3D>(id);
+    }
+
+    gobj = new Text3D();
+
+    gobj->setDataSet(NULL, this);
+
+    gobj->setText(string);
+    gobj->setFont(fontFamily);
+    gobj->setFontSize(fontSize);
+    gobj->setBold(bold);
+    gobj->setItalic(italic);
+    gobj->setShadow(shadow);
+
+    if (gobj->getProp() == NULL &&
+        gobj->getOverlayProp() == NULL) {
+        delete gobj;
+        return false;
+    } else {
+        if (gobj->getProp())
+            _renderer->AddViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->AddViewProp(gobj->getOverlayProp());
+    }
+
+    getGraphicsObjectHashmap<Text3D>()[id] = gobj;
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set Sphere resolution
+ */
+void Renderer::setSphereResolution(const DataSetId& id, int thetaRes, int phiRes)
+{
+    SphereHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _spheres.begin();
+        if (itr == _spheres.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _spheres.find(id);
+    }
+    if (itr == _spheres.end()) {
+        ERROR("Sphere not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setThetaResolution(thetaRes);
+        itr->second->setPhiResolution(phiRes);
+    } while (doAll && ++itr != _spheres.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set Sphere section
+ */
+void Renderer::setSphereSection(const DataSetId& id, double thetaStart, double thetaEnd,
+                                double phiStart, double phiEnd)
+{
+    SphereHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _spheres.begin();
+        if (itr == _spheres.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _spheres.find(id);
+    }
+    if (itr == _spheres.end()) {
+        ERROR("Sphere not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setStartTheta(thetaStart);
+        itr->second->setEndTheta(thetaEnd);
+        itr->second->setStartPhi(phiStart);
+        itr->second->setEndPhi(phiEnd);
+    } while (doAll && ++itr != _spheres.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed to points of the streamlines DataSet
+ */
+void Renderer::setStreamlinesNumberOfSeedPoints(const DataSetId& id, int numPoints)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setNumberOfSeedPoints(numPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed point size (may be a no-op)
+ */
+void Renderer::setStreamlinesSeedPointSize(const DataSetId& id, float size)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedPointSize(size);
+    } while (doAll && ++itr != _streamlines.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed to points of the streamlines DataSet
+ */
+void Renderer::setStreamlinesSeedToMeshPoints(const DataSetId& id,
+                                              int maxPoints)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedToMeshPoints(maxPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed to points distributed randomly inside
+ * cells of the streamlines DataSet
+ */
+void Renderer::setStreamlinesSeedToFilledMesh(const DataSetId& id, int numPoints)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedToFilledMesh(numPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed to points of a DataSet
+ *
+ * \param[in] id DataSet identifier
+ * \param[in] data Bytes of VTK DataSet file
+ * \param[in] nbytes Length of data array
+ * \param[in] maxPoints Maximum number of points to be used as seeds
+ *
+ * \return boolean indicating success or failure
+ */
+bool Renderer::setStreamlinesSeedToMeshPoints(const DataSetId& id,
+                                              char *data, size_t nbytes,
+                                              int maxPoints)
+{
+    vtkSmartPointer<vtkDataSetReader> reader = vtkSmartPointer<vtkDataSetReader>::New();
+    vtkSmartPointer<vtkCharArray> dataSetString = vtkSmartPointer<vtkCharArray>::New();
+
+    dataSetString->SetArray(data, nbytes, 1);
+    reader->SetInputArray(dataSetString);
+    reader->ReadFromInputStringOn();
+    reader->Update();
+
+    vtkSmartPointer<vtkDataSet> dataSet = reader->GetOutput();
+    if (dataSet == NULL) {
+        return false;
+    }
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return false;
+    }
+
+    do {
+        itr->second->setSeedToMeshPoints(dataSet, maxPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set the streamlines seed to points distributed randomly inside
+ * cells of DataSet
+ *
+ * \param[in] id DataSet identifier
+ * \param[in] data Bytes of VTK DataSet file
+ * \param[in] nbytes Length of data array
+ * \param[in] numPoints Number of random seed points to generate
+ *
+ * \return boolean indicating success or failure
+ */
+bool Renderer::setStreamlinesSeedToFilledMesh(const DataSetId& id,
+                                              char *data, size_t nbytes,
+                                              int numPoints)
+{
+    vtkSmartPointer<vtkDataSetReader> reader = vtkSmartPointer<vtkDataSetReader>::New();
+    vtkSmartPointer<vtkCharArray> dataSetString = vtkSmartPointer<vtkCharArray>::New();
+
+    dataSetString->SetArray(data, nbytes, 1);
+    reader->SetInputArray(dataSetString);
+    reader->ReadFromInputStringOn();
+    reader->Update();
+
+    vtkSmartPointer<vtkDataSet> dataSet = reader->GetOutput();
+    if (dataSet == NULL) {
+        return false;
+    }
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return false;
+    }
+
+    do {
+        itr->second->setSeedToFilledMesh(dataSet, numPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set the streamlines seed to points along a line
+ */
+void Renderer::setStreamlinesSeedToRake(const DataSetId& id,
+                                        double start[3], double end[3],
+                                        int numPoints)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedToRake(start, end, numPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed to points inside a disk, with optional
+ * hole
+ */
+void Renderer::setStreamlinesSeedToDisk(const DataSetId& id,
+                                        double center[3], double normal[3],
+                                        double radius, double innerRadius,
+                                        int numPoints)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedToDisk(center, normal, radius, innerRadius, numPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed to vertices of an n-sided polygon
+ */
+void Renderer::setStreamlinesSeedToPolygon(const DataSetId& id,
+                                           double center[3], double normal[3],
+                                           double angle, double radius,
+                                           int numSides)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedToPolygon(center, normal, angle, radius, numSides);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the streamlines seed to vertices of an n-sided polygon
+ */
+void Renderer::setStreamlinesSeedToFilledPolygon(const DataSetId& id,
+                                                 double center[3],
+                                                 double normal[3],
+                                                 double angle, double radius,
+                                                 int numSides, int numPoints)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedToFilledPolygon(center, normal, angle,
+                                            radius, numSides, numPoints);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::setStreamlinesTerminalSpeed(const DataSetId& id, double speed)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setTerminalSpeed(speed);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set Streamlines rendering to polylines for the specified DataSet
+ */
+void Renderer::setStreamlinesTypeToLines(const DataSetId& id)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setLineTypeToLines();
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set Streamlines rendering to tubes for the specified DataSet
+ */
+void Renderer::setStreamlinesTypeToTubes(const DataSetId& id, int numSides, double radius)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setLineTypeToTubes(numSides, radius);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set Streamlines rendering to ribbons for the specified DataSet
+ */
+void Renderer::setStreamlinesTypeToRibbons(const DataSetId& id, double width, double angle)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setLineTypeToRibbons(width, angle);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set Streamlines maximum length for the specified DataSet
+ */
+void Renderer::setStreamlinesLength(const DataSetId& id, double length)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setMaxPropagation(length);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off rendering of the Streamlines seed geometry for the given DataSet
+ */
+void Renderer::setStreamlinesSeedVisibility(const DataSetId& id, bool state)
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedVisibility(state);
+    } while (doAll && ++itr != _streamlines.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setStreamlinesColorMode(const DataSetId& id,
+                                       Streamlines::ColorMode mode,
+                                       DataSet::DataAttributeType type,
+                                       const char *name, double range[2])
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _streamlines.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setStreamlinesColorMode(const DataSetId& id,
+                                       Streamlines::ColorMode mode,
+                                       const char *name, double range[2])
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _streamlines.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the RGB line/edge color for the specified DataSet
+ */
+void Renderer::setStreamlinesSeedColor(const DataSetId& id, float color[3])
+{
+    StreamlinesHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _streamlines.begin();
+        if (itr == _streamlines.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _streamlines.find(id);
+    }
+    if (itr == _streamlines.end()) {
+        ERROR("Streamlines not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSeedColor(color);
+    } while (doAll && ++itr != _streamlines.end());
+
+    _needsRedraw = true;
+}
+
+void Renderer::setText3DBold(const DataSetId& id, bool state)
+{
+    Text3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _text3Ds.begin();
+        if (itr == _text3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _text3Ds.find(id);
+    }
+    if (itr == _text3Ds.end()) {
+        ERROR("Text3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBold(state);
+    } while (doAll && ++itr != _text3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the font family for the Text3D
+ */
+void Renderer::setText3DFont(const DataSetId& id, const char *fontFamily)
+{
+    Text3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _text3Ds.begin();
+        if (itr == _text3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _text3Ds.find(id);
+    }
+    if (itr == _text3Ds.end()) {
+        ERROR("Text3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setFont(fontFamily);
+    } while (doAll && ++itr != _text3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the font family for the Text3D
+ */
+void Renderer::setText3DFontSize(const DataSetId& id, int size)
+{
+    Text3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _text3Ds.begin();
+        if (itr == _text3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _text3Ds.find(id);
+    }
+    if (itr == _text3Ds.end()) {
+        ERROR("Text3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setFontSize(size);
+    } while (doAll && ++itr != _text3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::setText3DFollowCamera(const DataSetId& id, bool state)
+{
+    Text3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _text3Ds.begin();
+        if (itr == _text3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _text3Ds.find(id);
+    }
+    if (itr == _text3Ds.end()) {
+        ERROR("Text3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setFollowCamera(state, _renderer);
+    } while (doAll && ++itr != _text3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::setText3DItalic(const DataSetId& id, bool state)
+{
+    Text3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _text3Ds.begin();
+        if (itr == _text3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _text3Ds.find(id);
+    }
+    if (itr == _text3Ds.end()) {
+        ERROR("Text3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setItalic(state);
+    } while (doAll && ++itr != _text3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::setText3DShadow(const DataSetId& id, bool state)
+{
+    Text3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _text3Ds.begin();
+        if (itr == _text3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _text3Ds.find(id);
+    }
+    if (itr == _text3Ds.end()) {
+        ERROR("Text3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setShadow(state);
+    } while (doAll && ++itr != _text3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::setText3DText(const DataSetId& id, const char *text)
+{
+    Text3DHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _text3Ds.begin();
+        if (itr == _text3Ds.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _text3Ds.find(id);
+    }
+    if (itr == _text3Ds.end()) {
+        ERROR("Text3D not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setText(text);
+    } while (doAll && ++itr != _text3Ds.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+void Renderer::setVolumeBlendMode(const DataSetId& id, Volume::BlendMode mode)
+{
+    VolumeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _volumes.begin();
+        if (itr == _volumes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _volumes.find(id);
+    }
+    if (itr == _volumes.end()) {
+        ERROR("Volume not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setBlendMode(mode);
+    } while (doAll && ++itr != _volumes.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the sample rate for the volume shader
+ *
+ * Smaller values will give better rendering at the cost
+ * of slower rendering
+ */
+void Renderer::setVolumeSampleDistance(const DataSetId& id, double distance)
+{
+    VolumeHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _volumes.begin();
+        if (itr == _volumes.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _volumes.find(id);
+    }
+    if (itr == _volumes.end()) {
+        ERROR("Volume not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        distance *= itr->second->getAverageSpacing();
+        itr->second->setSampleDistance((float)distance);
+    } while (doAll && ++itr != _volumes.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the point cloud render style for the specified DataSet
+ */
+void Renderer::setWarpCloudStyle(const DataSetId& id,
+                                 Warp::CloudStyle style)
+{
+    WarpHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _warps.begin();
+        if (itr == _warps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _warps.find(id);
+    }
+    if (itr == _warps.end()) {
+        ERROR("Warp not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCloudStyle(style);
+    } while (doAll && ++itr != _warps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setWarpColorMode(const DataSetId& id,
+                                Warp::ColorMode mode,
+                                DataSet::DataAttributeType type,
+                                const char *name, double range[2])
+{
+    WarpHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _warps.begin();
+        if (itr == _warps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _warps.find(id);
+    }
+    if (itr == _warps.end()) {
+        ERROR("Warp not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, type, name, range);
+    } while (doAll && ++itr != _warps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the color mode for the specified DataSet
+ */
+void Renderer::setWarpColorMode(const DataSetId& id,
+                                Warp::ColorMode mode,
+                                const char *name, double range[2])
+{
+    WarpHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _warps.begin();
+        if (itr == _warps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _warps.find(id);
+    }
+    if (itr == _warps.end()) {
+        ERROR("Warp not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColorMode(mode, name, range);
+    } while (doAll && ++itr != _warps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set amount to scale vector magnitudes when warping
+ * a mesh
+ */
+void Renderer::setWarpWarpScale(const DataSetId& id, double scale)
+{
+    WarpHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _warps.begin();
+        if (itr == _warps.end())
+            return;
+        doAll = true;
+    } else {
+        itr = _warps.find(id);
+    }
+
+    if (itr == _warps.end()) {
+        ERROR("Warp not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setWarpScale(scale);
+     } while (doAll && ++itr != _warps.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
Index: vtkvis/tags/1.8.4/RendererGraphicsObjs.h
===================================================================
--- vtkvis/tags/1.8.4/RendererGraphicsObjs.h	(revision 6184)
+++ vtkvis/tags/1.8.4/RendererGraphicsObjs.h	(revision 6184)
@@ -0,0 +1,1196 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_RENDERER_GRAPHICS_OBJS_H
+#define VTKVIS_RENDERER_GRAPHICS_OBJS_H
+
+#include <tr1/unordered_map>
+#include <typeinfo>
+
+// Use GCC/G++ specific name demangling
+#define DEMANGLE
+
+#ifdef DEMANGLE
+#include <cstdlib>
+#include <cxxabi.h>
+#define GO_TRACE(go, format, ...)                                            \
+do {                                                                         \
+    int status;                                                              \
+    char * typeName = abi::__cxa_demangle(typeid(go).name(), 0, 0, &status); \
+    TRACE("%s " format, typeName, __VA_ARGS__);                              \
+    free(typeName);                                                          \
+} while (0)
+
+#define GO_ERROR(go, format, ...)                                            \
+do {                                                                         \
+    int status;                                                              \
+    char * typeName = abi::__cxa_demangle(typeid(go).name(), 0, 0, &status); \
+    ERROR("%s " format, typeName, __VA_ARGS__);                              \
+    free(typeName);                                                          \
+} while (0)
+#else
+#define GO_TRACE(go, format, ...) TRACE("%s " format, typeid(go).name(), __VA_ARGS__);
+#define GO_ERROR(go, format, ...) ERROR("%s " format, typeid(go).name(), __VA_ARGS__);
+#endif
+
+#include "Renderer.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Look up graphics object by name
+ */
+template<class T>
+T *Renderer::getGraphicsObject(const DataSetId& id)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap =
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator
+        itr = hashmap.find(id);
+
+    if (itr == hashmap.end()) {
+#ifdef DEBUG
+        GO_TRACE(T, "not found: %s", id.c_str());
+#endif
+        return NULL;
+    } else
+        return itr->second;
+}
+
+/**
+ * \brief Delete graphics object by name
+ */
+template<class T>
+void Renderer::deleteGraphicsObject(const DataSetId& id)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap =
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    TRACE("Deleting %s for %s", itr->second->getClassName(), id.c_str());
+
+    do {
+        T *gobj = itr->second;
+        if (gobj->getProp())
+            _renderer->RemoveViewProp(gobj->getProp());
+        if (gobj->getOverlayProp())
+            _renderer->RemoveViewProp(gobj->getOverlayProp());
+        delete gobj;
+
+        itr = hashmap.erase(itr);
+    } while (doAll && itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Delete all graphics objects from renderer
+ */
+template<class T>
+void Renderer::deleteAllGraphicsObjects()
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap =
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    itr = hashmap.begin();
+    if (itr == hashmap.end())
+        return;
+
+    TRACE("Deleting all %s objects", itr->second->getClassName());
+
+    for (; itr != hashmap.end(); ++itr) {
+        delete itr->second;
+    }
+    hashmap.clear();
+}
+
+/**
+ * \brief Add a graphics objects to the renderer's scene
+ */
+template<class T>
+bool Renderer::addGraphicsObject(const DataSetId& id)
+{
+    DataSetHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _dataSets.begin();
+    } else {
+        itr = _dataSets.find(id);
+    }
+    if (itr == _dataSets.end()) {
+        ERROR("Unknown dataset %s", id.c_str());
+        return false;
+    }
+
+    do {
+        DataSet *ds = itr->second;
+        const DataSetId& dsID = ds->getName();
+
+        T *gobj;
+        if ((gobj = getGraphicsObject<T>(dsID)) != NULL) {
+            WARN("Replacing existing %s %s", gobj->getClassName(), dsID.c_str());
+            deleteGraphicsObject<T>(dsID);
+        }
+
+        gobj = new T();
+ 
+        gobj->setDataSet(ds, this);
+
+        if (gobj->getProp() == NULL &&
+            gobj->getOverlayProp() == NULL) {
+            delete gobj;
+            return false;
+        } else {
+            if (gobj->getProp())
+                _renderer->AddViewProp(gobj->getProp());
+            if (gobj->getOverlayProp())
+                _renderer->AddViewProp(gobj->getOverlayProp());
+        }
+
+        getGraphicsObjectHashmap<T>()[dsID] = gobj;
+    } while (doAll && ++itr != _dataSets.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+    return true;
+}
+
+/**
+ * \brief Set the prop orientation with a quaternion
+ */
+template<class T>
+void Renderer::setGraphicsObjectTransform(const DataSetId& id, vtkMatrix4x4 *trans)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setTransform(trans);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the prop orientation with a quaternion
+ */
+template<class T>
+void Renderer::setGraphicsObjectOrientation(const DataSetId& id, double quat[4])
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOrientation(quat);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the prop orientation with a rotation about an axis
+ */
+template<class T>
+void Renderer::setGraphicsObjectOrientation(const DataSetId& id, double angle, double axis[3])
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOrientation(angle, axis);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the prop origin (center of rotation)
+ */
+template<class T>
+void Renderer::setGraphicsObjectOrigin(const DataSetId& id, double origin[3])
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOrigin(origin);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the prop position in world coords
+ */
+template<class T>
+void Renderer::setGraphicsObjectPosition(const DataSetId& id, double pos[3])
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setPosition(pos);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the prop scaling based on a 2D aspect ratio
+ *
+ * \param id The name of the DataSet
+ * \param aspect The aspect ratio (width/height), zero means native aspect
+ */
+template<class T>
+void Renderer::setGraphicsObjectAspect(const DataSetId& id, double aspect)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAspect(aspect);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the prop scaling
+ */
+template<class T>
+void Renderer::setGraphicsObjectScale(const DataSetId& id, double scale[3])
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setScale(scale);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set visibility of GraphicsObject for the given DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectVisibility(const DataSetId& id, bool state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap =
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setVisibility(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the volume slice
+ */
+template<class T>
+void Renderer::setGraphicsObjectVolumeSlice(const DataSetId& id, Axis axis, double ratio)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->selectVolumeSlice(axis, ratio);
+     } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+
+/**
+ * \brief Set the RGB actor color for the specified DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectColor(const DataSetId& id, float color[3])
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setColor(color);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set cull face for graphics object
+ */
+template<class T>
+void Renderer::setGraphicsObjectCullFace(const DataSetId& id,
+                                         GraphicsObject::CullFace state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCullFace(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set face culling for graphics object
+ */
+template<class T>
+void Renderer::setGraphicsObjectCulling(const DataSetId& id, bool state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setCulling(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn on/off edges for the given DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectEdgeVisibility(const DataSetId& id, bool state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setEdgeVisibility(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the RGB isosurface edge color for the specified DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectEdgeColor(const DataSetId& id, float color[3])
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setEdgeColor(color);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the isosurface edge width for the specified DataSet (may be a no-op)
+ *
+ * If the OpenGL implementation/hardware does not support wide lines, 
+ * this function may not have an effect.
+ */
+template<class T>
+void Renderer::setGraphicsObjectEdgeWidth(const DataSetId& id, float edgeWidth)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setEdgeWidth(edgeWidth);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Flip normals and front/back faces of the shape geometry
+ */
+template<class T>
+void Renderer::setGraphicsObjectFlipNormals(const DataSetId& id, bool state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->flipNormals(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set ambient lighting/shading coefficient for the specified DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectAmbient(const DataSetId& id, double coeff)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setAmbient(coeff);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set diffuse lighting/shading coefficient for the specified DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectDiffuse(const DataSetId& id, double coeff)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setDiffuse(coeff);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the shading of the object (flat or smooth)
+ *
+ * Currently Phong shading is not implemented
+ */
+template<class T>
+void Renderer::setGraphicsObjectShadingModel(const DataSetId& id,
+                                             GraphicsObject::ShadingModel state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setShadingModel(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set specular lighting/shading coefficient and power for the specified DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectSpecular(const DataSetId& id, double coeff, double power)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setSpecular(coeff, power);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Turn actor lighting on/off for the specified DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectLighting(const DataSetId& id, bool state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setLighting(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set opacity of GraphicsObject for the given DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectOpacity(const DataSetId& id, double opacity)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setOpacity(opacity);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set the point size for the specified DataSet (may be a no-op)
+ *
+ * If the OpenGL implementation/hardware does not support wide points, 
+ * this function may not have an effect.
+ */
+template<class T>
+void Renderer::setGraphicsObjectPointSize(const DataSetId& id, float size)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setPointSize(size);
+    } while (doAll && ++itr != hashmap.end());
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set wireframe actor rendering for the specified DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectWireframe(const DataSetId& id, bool state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setWireframe(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Associate an existing named color map with a graphics object for the given DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectInterpolateBeforeMapping(const DataSetId& id, bool state)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    do {
+        itr->second->setInterpolateBeforeMapping(state);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Associate an existing named color map with a graphics object for the given DataSet
+ */
+template<class T>
+void Renderer::setGraphicsObjectColorMap(const DataSetId& id, const ColorMapId& colorMapId)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = hashmap.begin();
+        if (itr == hashmap.end())
+            return;
+        doAll = true;
+    } else {
+        itr = hashmap.find(id);
+    }
+
+    if (itr == hashmap.end()) {
+        GO_ERROR(T, "not found: %s", id.c_str());
+        return;
+    }
+
+    ColorMap *cmap = getColorMap(colorMapId);
+    if (cmap == NULL && colorMapId.compare("none") != 0) {
+        ERROR("Unknown colormap: %s", colorMapId.c_str());
+        return;
+    }
+
+    do {
+        GO_TRACE(T, "Set color map: %s for dataset %s",
+                 colorMapId.c_str(),
+                 itr->second->getDataSet()->getName().c_str());
+
+        itr->second->setColorMap(cmap);
+    } while (doAll && ++itr != hashmap.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Notify graphics object that color map has changed
+ */
+template<class T>
+void Renderer::updateGraphicsObjectColorMap(ColorMap *cmap)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    for (itr = hashmap.begin(); itr != hashmap.end(); ++itr) {
+        if (itr->second->getColorMap() == cmap) {
+            itr->second->updateColorMap();
+            _needsRedraw = true;
+        }
+    }
+}
+
+/**
+ * \brief Check if a color map is in use by a graphics object
+ */
+template<class T>
+bool Renderer::graphicsObjectColorMapUsed(ColorMap *cmap)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    for (itr = hashmap.begin(); itr != hashmap.end(); ++itr) {
+        if (itr->second->getColorMap() == cmap)
+            return true;
+    }
+    return false;
+}
+
+/**
+ * \brief Compute union of bounds and GO's bounds
+ */
+template<class T>
+void Renderer::mergeGraphicsObjectBounds(double *bounds, bool onlyVisible)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    for (itr = hashmap.begin(); itr != hashmap.end(); ++itr) {
+        if ((!onlyVisible || itr->second->getVisibility()) &&
+            itr->second->getProp() != NULL) {
+            mergeBounds(bounds, bounds, itr->second->getBounds());
+#ifdef DEBUG
+            double *bPtr = itr->second->getBounds();
+            assert(bPtr != NULL);
+            GO_TRACE(T,
+                     "%s bounds: %g %g %g %g %g %g",
+                     itr->first.c_str(),
+                     bPtr[0], bPtr[1], bPtr[2], bPtr[3], bPtr[4], bPtr[5]);
+#endif
+        }
+    }
+}
+
+/**
+ * \brief Compute union of bounds and GO's unscaled bounds
+ *
+ * Unscaled bounds are the bounds of the object before any actor scaling is
+ * applied
+ */
+template<class T>
+void Renderer::mergeGraphicsObjectUnscaledBounds(double *bounds, bool onlyVisible)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    for (itr = hashmap.begin(); itr != hashmap.end(); ++itr) {
+        if ((!onlyVisible || itr->second->getVisibility()) &&
+            itr->second->getProp() != NULL) {
+            mergeBounds(bounds, bounds, itr->second->getUnscaledBounds());
+#ifdef DEBUG
+            double *bPtr = itr->second->getUnscaledBounds();
+            assert(bPtr != NULL);
+            GO_TRACE(T,
+                     "%s bounds: %g %g %g %g %g %g",
+                     itr->first.c_str(),
+                     bPtr[0], bPtr[1], bPtr[2], bPtr[3], bPtr[4], bPtr[5]);
+#endif
+        }
+    }
+}
+
+/**
+ * \brief Notify object that field value ranges have changed
+ */
+template<class T>
+void Renderer::updateGraphicsObjectFieldRanges()
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    for (itr = hashmap.begin(); itr != hashmap.end(); ++itr) {
+        itr->second->updateRanges(this);
+    }
+}
+
+/**
+ * \brief Pass global clip planes to graphics object
+ */
+template<class T>
+void Renderer::setGraphicsObjectClippingPlanes(vtkPlaneCollection *planes)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    for (itr = hashmap.begin(); itr != hashmap.end(); ++itr) {
+        itr->second->setClippingPlanes(planes);
+    }
+}
+
+/**
+ * \brief Set the prop scaling based on a 2D aspect ratio
+ *
+ * This method sets the aspect on all graphics objects of a given type
+ *
+ * \param aspectRatio The aspect ratio (width/height), zero means native aspect
+ */
+template<class T>
+void Renderer::setGraphicsObjectAspect(double aspectRatio)
+{
+    std::tr1::unordered_map<DataSetId, T *>& hashmap = 
+        getGraphicsObjectHashmap<T>();
+    typename std::tr1::unordered_map<DataSetId, T *>::iterator itr;
+
+    for (itr = hashmap.begin(); itr != hashmap.end(); ++itr) {
+        itr->second->setAspect(aspectRatio);
+    }
+
+    sceneBoundsChanged();
+    _needsRedraw = true;
+}
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/ResponseQueue.cpp
===================================================================
--- vtkvis/tags/1.8.4/ResponseQueue.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/ResponseQueue.cpp	(revision 6184)
@@ -0,0 +1,109 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <cerrno>
+#include <cstring>
+#include <cstdlib>
+#include <list>
+
+#include "Trace.h"
+#include "ResponseQueue.h"
+
+using namespace VtkVis;
+
+ResponseQueue::ResponseQueue()
+{
+    pthread_mutex_init(&_idle, NULL);
+    if (sem_init(&_ready, 0, 0) < 0) {
+        ERROR("can't initialize semaphore: %s", strerror(errno));
+    }
+}
+
+ResponseQueue::~ResponseQueue() 
+{
+    TRACE("Deleting ResponseQueue");
+    pthread_mutex_destroy(&_idle);
+    if (sem_destroy(&_ready) < 0) {
+        ERROR("can't destroy semaphore: %s", strerror(errno));
+    }
+    for (std::list<Response *>::iterator itr = _list.begin();
+         itr != _list.end(); ++itr) {
+        delete *itr;
+    }
+    _list.clear();
+}
+
+void
+ResponseQueue::enqueue(Response *response)
+{
+    if (pthread_mutex_lock(&_idle) != 0) {
+        ERROR("can't lock mutex: %s", strerror(errno));
+    }	
+    /* Examine the list and remove any queued responses of the same type. */
+    TRACE("Before # of elements is %d", _list.size());
+    for (std::list<Response *>::iterator itr = _list.begin();
+         itr != _list.end();) {
+        /* Remove any duplicate image or legend responses. There should be no 
+         * more than one.  Note that if the client starts using multiple legends 
+         * for different fields, this optimization should be disabled for legends.
+         * We may also need to differentiate between screen images and "hardcopy"
+         * images.
+         */
+        if ((response->type() == Response::IMAGE ||
+             response->type() == Response::LEGEND) &&
+            (*itr)->type() == response->type()) {
+            TRACE("Removing duplicate response of type: %d", (*itr)->type());
+            delete *itr;
+            itr = _list.erase(itr);
+        } else {
+            TRACE("Found queued response of type %d", (*itr)->type());
+            ++itr;
+        }
+    }
+    /* Add the new response to the end of the list. */
+    _list.push_back(response);
+    TRACE("After # of elements is %d", _list.size());
+    if (sem_post(&_ready) < 0) {
+        ERROR("can't post semaphore: %s", strerror(errno));
+    }
+    if (pthread_mutex_unlock(&_idle) != 0) {
+        ERROR("can't unlock mutex: %s", strerror(errno));
+    }	
+}
+
+Response *
+ResponseQueue::dequeue()
+{
+    Response *response = NULL;
+
+    int ret;
+    while ((ret = sem_wait(&_ready)) < 0 && errno == EINTR)
+        continue;
+    if (ret < 0) {
+        ERROR("can't wait on semaphore: %s", strerror(errno));
+    }
+    if (pthread_mutex_lock(&_idle) != 0) {
+        ERROR("can't lock mutex: %s", strerror(errno));
+    }
+    /* List may be empty if semaphore value was incremented beyond list
+     * size in the case of an enqueue operation deleting from the queue
+     * This is not an error, so just unlock and return to wait loop
+     */
+    if (_list.empty()) {
+        TRACE("Empty queue");
+    } else {
+        response = _list.front();
+        _list.pop_front();
+        TRACE("Dequeued response of type %d", response->type());
+    }
+    if (pthread_mutex_unlock(&_idle) != 0) {
+        ERROR("can't unlock mutex: %s", strerror(errno));
+    }	
+    return response;
+}
Index: vtkvis/tags/1.8.4/ResponseQueue.h
===================================================================
--- vtkvis/tags/1.8.4/ResponseQueue.h	(revision 6184)
+++ vtkvis/tags/1.8.4/ResponseQueue.h	(revision 6184)
@@ -0,0 +1,130 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+#ifndef VTKVIS_RESPONSE_QUEUE_H
+#define VTKVIS_RESPONSE_QUEUE_H
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <cstdlib>
+#include <cstring>
+#include <list>
+
+namespace VtkVis {
+
+/**
+ * \brief Holds data for a response to be sent to client
+ */
+class Response
+{
+public:
+    enum AllocationType {
+        STATIC,
+        DYNAMIC,
+        VOLATILE
+    };
+    enum ResponseType {
+        IMAGE,          /**< Image to be displayed. */
+        LEGEND,         /**< Legend to be displayed. */
+        DATA,           /**< Any other type of non-error message. */
+        ERROR,          /**< Error message. */
+        OK              /**< Ack for client when no render required */
+    };
+
+    Response(ResponseType type) :
+        _mesg(NULL),
+        _length(0),
+        _type(type)
+    {
+    }
+
+    virtual ~Response()
+    {
+	if ((_length > 0) && (_mesg != NULL) && (_allocType == DYNAMIC)) {
+            free(_mesg);
+	}
+    }
+
+    /// Get the ResponseType
+    ResponseType type()
+    {
+	return _type;
+    }
+
+    /// Get the Response data
+    unsigned char *message()
+    {
+	return _mesg;
+    }
+
+    /// Get the number of bytes in the Response data
+    size_t length()
+    {
+	return _length;
+    }
+
+    /// Set the message/data making up the Response
+    /**
+     * If the AllocationType is DYNAMIC, the message data will be free()d
+     * by the destructor.  If the AllocationType is VOLATILE, a copy of 
+     * the message data will be made.
+     *
+     * \param[in] mesg The Response data, can be a command and/or binary data
+     * \param[in] length The number of bytes in mesg
+     * \param[in] type Specify how the memory was allocated for mesg
+     */ 
+    void setMessage(unsigned char *mesg, size_t length, AllocationType type)
+    {
+	if (type == VOLATILE) {
+	    _length = length;
+	    _mesg = (unsigned char *)malloc(length);
+	    memcpy(_mesg, mesg, length);
+	    _allocType = DYNAMIC;
+	} else {
+	    _length = length;
+	    _mesg = mesg;
+	    _allocType = type;
+	}
+    }
+
+private:
+    /** 
+     * (Malloc-ed by caller, freed by destructor)
+     * Contains the message/bytes to be sent to the client. */
+    unsigned char *_mesg;
+    size_t _length;       /**< # of bytes in the above message. */
+    ResponseType _type;
+    AllocationType _allocType;
+};
+
+/**
+ * \brief Queue to hold pending Responses to be sent to the client
+ *
+ * A semaphore and mutex are used to control access to the 
+ * queue by a reader and writer thread
+ */
+class ResponseQueue
+{
+public:
+    ResponseQueue();
+
+    virtual ~ResponseQueue();
+
+    /// Add a response to the end of the queue
+    void enqueue(Response *response);
+
+    /// Remove a response from the front of the queue
+    Response *dequeue();
+
+private:
+    pthread_mutex_t _idle;
+    sem_t _ready; /**< Semaphore indicating that a response has been queued. */
+    std::list<Response *> _list;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Shape.cpp
===================================================================
--- vtkvis/tags/1.8.4/Shape.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Shape.cpp	(revision 6184)
@@ -0,0 +1,42 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkPolyData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkTransform.h>
+
+#include "Shape.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Shape::Shape() :
+    GraphicsObject()
+{
+}
+
+Shape::~Shape()
+{
+    //TRACE("Deleting Shape");
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Shape::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_pdMapper != NULL) {
+        _pdMapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Shape.h
===================================================================
--- vtkvis/tags/1.8.4/Shape.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Shape.h	(revision 6184)
@@ -0,0 +1,52 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_SHAPE_H
+#define VTKVIS_SHAPE_H
+
+#include <cassert>
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+
+#include "GraphicsObject.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK Mesh (Polygon data)
+ *
+ * This class creates a boundary mesh of a DataSet
+ */
+class Shape : public GraphicsObject {
+public:
+    Shape();
+    virtual ~Shape();
+
+    virtual const char *getClassName() const
+    {
+        return "Shape";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer)
+    {
+        assert(dataSet == NULL);
+        update();
+    }
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+protected:
+    vtkSmartPointer<vtkPolyDataMapper> _pdMapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Sphere.cpp
===================================================================
--- vtkvis/tags/1.8.4/Sphere.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Sphere.cpp	(revision 6184)
@@ -0,0 +1,69 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkPolyData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkTransform.h>
+#include <vtkSphereSource.h>
+#include <vtkReverseSense.h>
+
+#include "Sphere.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Sphere::Sphere() :
+    Shape()
+{
+}
+
+Sphere::~Sphere()
+{
+    TRACE("Deleting Sphere");
+}
+
+void Sphere::update()
+{
+    if (_sphere == NULL) {
+	_sphere = vtkSmartPointer<vtkSphereSource>::New();
+    }
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOff();
+    }
+
+    _pdMapper->SetInputConnection(_sphere->GetOutputPort());
+
+    initProp();
+
+    getActor()->SetMapper(_pdMapper);
+    _pdMapper->Update();
+}
+
+void Sphere::flipNormals(bool state)
+{
+    if (_sphere == NULL || _pdMapper == NULL)
+        return;
+
+    if (state) {
+        vtkSmartPointer<vtkReverseSense> filter = vtkSmartPointer<vtkReverseSense>::New();
+        filter->ReverseCellsOn();
+        filter->ReverseNormalsOn();
+        filter->SetInputConnection(_sphere->GetOutputPort());
+
+        _pdMapper->SetInputConnection(filter->GetOutputPort());
+    } else {
+        _pdMapper->SetInputConnection(_sphere->GetOutputPort());
+    }
+}
Index: vtkvis/tags/1.8.4/Sphere.h
===================================================================
--- vtkvis/tags/1.8.4/Sphere.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Sphere.h	(revision 6184)
@@ -0,0 +1,101 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_SPHERE_H
+#define VTKVIS_SPHERE_H
+
+#include <vtkSmartPointer.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkSphereSource.h>
+
+#include "Shape.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief VTK PolyData sphere
+ *
+ * This class creates a mesh sphere
+ */
+class Sphere : public Shape
+{
+public:
+    Sphere();
+    virtual ~Sphere();
+
+    virtual const char *getClassName() const
+    {
+        return "Sphere";
+    }
+
+    void setThetaResolution(int res)
+    {
+        if (_sphere != NULL)
+            _sphere->SetThetaResolution(res);
+    }
+
+    void setPhiResolution(int res)
+    {
+        if (_sphere != NULL)
+            _sphere->SetPhiResolution(res);
+    }
+
+    void setStartTheta(double val)
+    {
+        if (_sphere != NULL)
+            _sphere->SetStartTheta(val);
+    }
+
+    void setEndTheta(double val)
+    {
+        if (_sphere != NULL)
+            _sphere->SetEndTheta(val);
+    }
+
+    void setStartPhi(double val)
+    {
+        if (_sphere != NULL)
+            _sphere->SetStartPhi(val);
+    }
+
+    void setEndPhi(double val)
+    {
+        if (_sphere != NULL)
+            _sphere->SetEndPhi(val);
+    }
+
+    void setLatLongTessellation(bool val)
+    {
+        if (_sphere != NULL)
+            _sphere->SetLatLongTessellation((val ? 1 : 0));
+    }
+
+    void setCenter(double center[3])
+    {
+        if (_sphere != NULL)
+            _sphere->SetCenter(center);
+    }
+
+    void setRadius(double radius)
+    {
+        if (_sphere != NULL)
+            _sphere->SetRadius(radius);
+    }
+
+    void flipNormals(bool state);
+
+private:
+    virtual void update();
+
+    vtkSmartPointer<vtkSphereSource> _sphere;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Streamlines.cpp
===================================================================
--- vtkvis/tags/1.8.4/Streamlines.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Streamlines.cpp	(revision 6184)
@@ -0,0 +1,1741 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cstdlib>
+#include <ctime>
+#include <cfloat>
+#include <cmath>
+#include <cstring>
+
+#include <vtkMath.h>
+#include <vtkActor.h>
+#include <vtkProperty.h>
+#include <vtkPoints.h>
+#include <vtkCellArray.h>
+#include <vtkPolyLine.h>
+#include <vtkRegularPolygonSource.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCellDataToPointData.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkPolygon.h>
+#include <vtkPolyData.h>
+#include <vtkTubeFilter.h>
+#include <vtkRibbonFilter.h>
+#include <vtkTransform.h>
+#include <vtkTransformPolyDataFilter.h>
+#include <vtkVertexGlyphFilter.h>
+#include <vtkMaskPoints.h>
+
+#include "Streamlines.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Streamlines::Streamlines() :
+    GraphicsObject(),
+    _lineType(LINES),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_VECTOR_MAGNITUDE),
+    _colorFieldType(DataSet::POINT_DATA),
+    _seedVisible(true),
+    _renderer(NULL),
+    _dataScale(1)
+{
+    _faceCulling = true;
+    _color[0] = 1.0f;
+    _color[1] = 1.0f;
+    _color[2] = 1.0f;
+    _seedColor[0] = 1.0f;
+    _seedColor[1] = 1.0f;
+    _seedColor[2] = 1.0f;
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+    vtkMath::RandomSeed((int)time(NULL));
+    srand((unsigned int)time(NULL));
+}
+
+Streamlines::~Streamlines()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Streamlines for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Streamlines with NULL DataSet");
+#endif
+}
+
+void Streamlines::setDataSet(DataSet *dataSet,
+                             Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Create and initialize a VTK Prop to render Streamlines
+ */
+void Streamlines::initProp()
+{
+    if (_linesActor == NULL) {
+        _linesActor = vtkSmartPointer<vtkActor>::New();
+        _linesActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
+        _linesActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+        _linesActor->GetProperty()->SetLineWidth(_edgeWidth);
+        _linesActor->GetProperty()->SetOpacity(_opacity);
+        _linesActor->GetProperty()->SetAmbient(.2);
+        if (!_lighting)
+            _linesActor->GetProperty()->LightingOff();
+        switch (_lineType) {
+        case LINES:
+            setCulling(_linesActor->GetProperty(), false);
+            _linesActor->GetProperty()->SetRepresentationToWireframe();
+            _linesActor->GetProperty()->EdgeVisibilityOff();
+            break;
+        case TUBES:
+            if (_faceCulling && _opacity == 1.0)
+                setCulling(_linesActor->GetProperty(), true);
+            _linesActor->GetProperty()->SetRepresentationToSurface();
+            _linesActor->GetProperty()->EdgeVisibilityOff();
+            break;
+        case RIBBONS:
+            setCulling(_linesActor->GetProperty(), false);
+            _linesActor->GetProperty()->SetRepresentationToSurface();
+            _linesActor->GetProperty()->EdgeVisibilityOff();
+            break;
+        default:
+            ;
+        }
+    }
+    if (_seedActor == NULL) {
+        _seedActor = vtkSmartPointer<vtkActor>::New();
+        _seedActor->GetProperty()->SetColor(_seedColor[0], _seedColor[1], _seedColor[2]);
+        _seedActor->GetProperty()->SetEdgeColor(_seedColor[0], _seedColor[1], _seedColor[2]);
+        _seedActor->GetProperty()->SetLineWidth(1);
+        _seedActor->GetProperty()->SetPointSize(2);
+        _seedActor->GetProperty()->SetOpacity(_opacity);
+        _seedActor->GetProperty()->SetRepresentationToWireframe();
+        _seedActor->GetProperty()->LightingOff();
+        setSeedVisibility(_seedVisible);
+    }
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkAssembly>::New();
+        getAssembly()->AddPart(_linesActor);
+        getAssembly()->AddPart(_seedActor);
+    }
+}
+
+/**
+ * \brief Get a pseudo-random number in range [min,max]
+ */
+double Streamlines::getRandomNum(double min, double max)
+{
+#if 1
+    return vtkMath::Random(min, max);
+#else
+    int r = rand();
+    return (min + ((double)r / RAND_MAX) * (max - min));
+#endif
+}
+
+/**
+ * \brief Get a random 3D point within an AABB
+ *
+ * \param[out] pt The random point
+ * \param[in] bounds The bounds of the AABB
+ */
+void Streamlines::getRandomPoint(double pt[3], const double bounds[6])
+{
+    pt[0] = getRandomNum(bounds[0], bounds[1]);
+    pt[1] = getRandomNum(bounds[2], bounds[3]);
+    pt[2] = getRandomNum(bounds[4], bounds[5]);
+}
+
+/**
+ * \brief Get a random point within a triangle (including edges)
+ *
+ * \param[out] pt The random point
+ * \param[in] v1 Triangle vertex 1
+ * \param[in] v2 Triangle vertex 2
+ * \param[in] v3 Triangle vertex 3
+ */
+void Streamlines::getRandomPointInTriangle(double pt[3],
+                                           const double v0[3],
+                                           const double v1[3],
+                                           const double v2[3])
+{
+    // Choose random barycentric coordinates
+    double bary[3];
+    bary[0] = getRandomNum(0, 1);
+    bary[1] = getRandomNum(0, 1);
+    if (bary[0] + bary[1] > 1.0) {
+        bary[0] = 1.0 - bary[0];
+        bary[1] = 1.0 - bary[1];
+    }
+    bary[2] = 1.0 - bary[0] - bary[1];
+
+    //TRACE("bary %g %g %g", bary[0], bary[1], bary[2]);
+    // Convert to cartesian coords
+    for (int i = 0; i < 3; i++) {
+        pt[i] = v0[i] * bary[0] + v1[i] * bary[1] + v2[i] * bary[2];
+    }
+}
+
+void Streamlines::getRandomPointInTetrahedron(double pt[3],
+                                              const double v0[3],
+                                              const double v1[3],
+                                              const double v2[3],
+                                              const double v3[3])
+{
+    // Choose random barycentric coordinates
+    double bary[4];
+    bary[0] = getRandomNum(0, 1);
+    bary[1] = getRandomNum(0, 1);
+    bary[2] = getRandomNum(0, 1);
+    if (bary[0] + bary[1] > 1.0) {
+        bary[0] = 1.0 - bary[0];
+        bary[1] = 1.0 - bary[1];
+    }
+    if (bary[1] + bary[2] > 1.0) {
+        double tmp = bary[2];
+        bary[2] = 1.0 - bary[0] - bary[1];
+        bary[1] = 1.0 - tmp;
+    } else if (bary[0] + bary[1] + bary[2] > 1.0) {
+        double tmp = bary[2];
+        bary[2] = bary[0] + bary[1] + bary[2] - 1.0;
+        bary[0] = 1.0 - bary[1] - tmp;
+    }
+    bary[3] = 1.0 - bary[0] - bary[1] - bary[2];
+    //TRACE("bary %g %g %g %g", bary[0], bary[1], bary[2], bary[3]);
+    // Convert to cartesian coords
+    for (int i = 0; i < 3; i++) {
+#if 0
+        pt[i] = (v0[i] - v3[i]) * bary[0] +
+                (v1[i] - v3[i]) * bary[1] + 
+                (v2[i] - v3[i]) * bary[2] + v3[i];
+#else
+        pt[i] = v0[i] * bary[0] + v1[i] * bary[1] +
+                v2[i] * bary[2] + v3[i] * bary[3];
+#endif
+    }
+}
+
+/**
+ * \brief Get a random point on a line segment (including endpoints)
+ */
+void Streamlines::getRandomPointOnLineSegment(double pt[3],
+                                              const double endpt[3],
+                                              const double endpt2[3])
+{
+    double ratio = getRandomNum(0, 1);
+    pt[0] = endpt[0] + ratio * (endpt2[0] - endpt[0]);
+    pt[1] = endpt[1] + ratio * (endpt2[1] - endpt[1]);
+    pt[2] = endpt[2] + ratio * (endpt2[2] - endpt[2]);
+}
+
+/**
+ * \brief Get a random point within a vtkDataSet's mesh
+ *
+ * Note: This currently doesn't always give a uniform distribution 
+ * of points in space and can generate points outside the mesh for
+ * unusual cell types
+ */
+void Streamlines::getRandomCellPt(double pt[3], vtkDataSet *ds)
+{
+    int numCells = (int)ds->GetNumberOfCells();
+    // XXX: Not uniform distribution (shouldn't use mod, and assumes
+    // all cells are equal area/volume)
+    int cell = rand() % numCells;
+    int type = ds->GetCellType(cell);
+    switch (type) {
+    case VTK_VERTEX: {
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() == 1);
+        ds->GetPoint(ptIds->GetId(0), pt);
+    }
+        break;
+    case VTK_POLY_VERTEX: {
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() >= 1);
+        int id = rand() % ptIds->GetNumberOfIds();
+        ds->GetPoint(ptIds->GetId(id), pt);
+    }
+        break;
+    case VTK_LINE: {
+        double v[2][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() == 2);
+        for (int i = 0; i < 2; i++) {
+            ds->GetPoint(ptIds->GetId(i), v[i]);
+        }
+        getRandomPointOnLineSegment(pt, v[0], v[1]);
+    }
+        break;
+    case VTK_POLY_LINE: {
+        double v[2][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() >= 2);
+        int id = rand() % (ptIds->GetNumberOfIds()-1);
+        for (int i = 0; i < 2; i++) {
+            ds->GetPoint(ptIds->GetId(id+i), v[i]);
+        }
+        getRandomPointOnLineSegment(pt, v[0], v[1]);
+    }
+        break;
+    case VTK_TRIANGLE: {
+        double v[3][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() == 3);
+        for (int i = 0; i < 3; i++) {
+            ds->GetPoint(ptIds->GetId(i), v[i]);
+        }
+        getRandomPointInTriangle(pt, v[0], v[1], v[2]);
+    }
+        break;
+    case VTK_TRIANGLE_STRIP: {
+        double v[3][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() >= 3);
+        int id = rand() % (ptIds->GetNumberOfIds()-2);
+        for (int i = 0; i < 3; i++) {
+            ds->GetPoint(ptIds->GetId(id+i), v[i]);
+        }
+        getRandomPointInTriangle(pt, v[0], v[1], v[2]);
+    }
+        break;
+    case VTK_POLYGON: {
+        vtkPolygon *poly = vtkPolygon::SafeDownCast(ds->GetCell(cell));
+        assert (poly != NULL);
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        poly->Triangulate(ptIds);
+        assert(ptIds->GetNumberOfIds() >= 3 && ptIds->GetNumberOfIds() % 3 == 0);
+        int tri = rand() % (ptIds->GetNumberOfIds()/3);
+        double v[3][3];
+        for (int i = 0; i < 3; i++) {
+            ds->GetPoint(ptIds->GetId(i + tri * 3), v[i]);
+        }
+        getRandomPointInTriangle(pt, v[0], v[1], v[2]);
+    }
+        break;
+    case VTK_QUAD: {
+        double v[4][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() == 4);
+        for (int i = 0; i < 4; i++) {
+            ds->GetPoint(ptIds->GetId(i), v[i]);
+        }
+        int tri = rand() & 0x1;
+        if (tri) {
+            getRandomPointInTriangle(pt, v[0], v[1], v[2]);
+        } else {
+            getRandomPointInTriangle(pt, v[0], v[2], v[3]);
+        }
+    }
+        break;
+    case VTK_TETRA: {
+        double v[4][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() == 4);
+        for (int i = 0; i < 4; i++) {
+            ds->GetPoint(ptIds->GetId(i), v[i]);
+        }
+        getRandomPointInTetrahedron(pt, v[0], v[1], v[2], v[3]);
+    }
+        break;
+    case VTK_WEDGE: {
+        double v[6][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() == 6);
+        for (int i = 0; i < 6; i++) {
+            ds->GetPoint(ptIds->GetId(i), v[i]);
+        }
+        double vv[3][3];
+        getRandomPointOnLineSegment(vv[0], v[0], v[3]);
+        getRandomPointOnLineSegment(vv[1], v[1], v[4]);
+        getRandomPointOnLineSegment(vv[2], v[2], v[5]);
+        getRandomPointInTriangle(pt, vv[0], vv[1], vv[2]);
+    }
+        break;
+    case VTK_PYRAMID: {
+        double v[5][3];
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        ds->GetCellPoints(cell, ptIds);
+        assert(ptIds->GetNumberOfIds() == 5);
+        for (int i = 0; i < 5; i++) {
+            ds->GetPoint(ptIds->GetId(i), v[i]);
+        }
+        int tetra = rand() & 0x1;
+        if (tetra) {
+            getRandomPointInTetrahedron(pt, v[0], v[1], v[2], v[4]);
+        } else {
+            getRandomPointInTetrahedron(pt, v[0], v[2], v[3], v[4]);
+        }
+    }
+        break;
+    case VTK_PIXEL:
+    case VTK_VOXEL: {
+        double bounds[6];
+        ds->GetCellBounds(cell, bounds);
+        getRandomPoint(pt, bounds);
+    }
+        break;
+    default: {
+        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
+        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
+        ds->GetCell(cell)->Triangulate(0, ptIds, pts);
+        if (ptIds->GetNumberOfIds() % 4 == 0) {
+            int tetra = rand() % (ptIds->GetNumberOfIds()/4);
+            double v[4][3];
+            for (int i = 0; i < 4; i++) {
+                ds->GetPoint(ptIds->GetId(i + tetra * 4), v[i]);
+            }
+            getRandomPointInTetrahedron(pt, v[0], v[1], v[2], v[4]);
+        } else {
+            assert(ptIds->GetNumberOfIds() % 3 == 0);
+            int tri = rand() % (ptIds->GetNumberOfIds()/3);
+            double v[3][3];
+            for (int i = 0; i < 3; i++) {
+                ds->GetPoint(ptIds->GetId(i + tri * 3), v[i]);
+            }
+            getRandomPointInTriangle(pt, v[0], v[1], v[2]);
+        }
+    }
+    }
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void Streamlines::update()
+{
+    if (_dataSet == NULL) {
+        return;
+    }
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    double bounds[6];
+    _dataSet->getBounds(bounds);
+    double xLen = bounds[1] - bounds[0];
+    double yLen = bounds[3] - bounds[2];
+    double zLen = bounds[5] - bounds[4];
+    double maxBound = 0.0;
+    if (xLen > maxBound) {
+        maxBound = xLen;
+    }
+    if (yLen > maxBound) {
+        maxBound = yLen;
+    }
+    if (zLen > maxBound) {
+        maxBound = zLen;
+    }
+
+    double cellSizeRange[2];
+    double avgSize;
+    _dataSet->getCellSizeRange(cellSizeRange, &avgSize);
+    _dataScale = avgSize / 8.;
+
+    vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
+
+    if (ds->GetPointData() == NULL ||
+        ds->GetPointData()->GetVectors() == NULL) {
+        TRACE("No vector point data found in DataSet %s", _dataSet->getName().c_str());
+        if (ds->GetCellData() != NULL &&
+            ds->GetCellData()->GetVectors() != NULL) {
+            cellToPtData = 
+                vtkSmartPointer<vtkCellDataToPointData>::New();
+            cellToPtData->SetInputData(ds);
+            cellToPtData->PassCellDataOn();
+            cellToPtData->Update();
+            ds = cellToPtData->GetOutput();
+        } else {
+            USER_ERROR("No vector field was found in the data set.");
+            return;
+        }
+    }
+
+    if (_streamTracer == NULL) {
+        _streamTracer = vtkSmartPointer<vtkStreamTracer>::New();
+    }
+
+    if (_dataSet->isCloud()) {
+        // DataSet is a point cloud
+        PrincipalPlane plane;
+        double offset;
+        vtkDataSet *mesherOutput = NULL;
+        if (_dataSet->is2D(&plane, &offset)) {
+            // Generate a 2D PolyData
+            _mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+            if (plane == PLANE_ZY) {
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->RotateWXYZ(90, 0, 1, 0);
+                if (offset != 0.0) {
+                    trans->Translate(-offset, 0, 0);
+                }
+                vtkDelaunay2D::SafeDownCast(_mesher)->SetTransform(trans);
+            } else if (plane == PLANE_XZ) {
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->RotateWXYZ(-90, 1, 0, 0);
+                if (offset != 0.0) {
+                    trans->Translate(0, -offset, 0);
+                }
+                vtkDelaunay2D::SafeDownCast(_mesher)->SetTransform(trans);
+            } else if (offset != 0.0) {
+                // XY with Z offset
+                vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                trans->Translate(0, 0, -offset);
+                vtkDelaunay2D::SafeDownCast(_mesher)->SetTransform(trans);
+            }
+            vtkDelaunay2D::SafeDownCast(_mesher)->SetInputData(ds);
+            _streamTracer->SetInputConnection(_mesher->GetOutputPort());
+            _mesher->Update();
+            mesherOutput = vtkDelaunay2D::SafeDownCast(_mesher)->GetOutput();
+        } else {
+            // Generate a 3D unstructured grid
+            _mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+            vtkDelaunay3D::SafeDownCast(_mesher)->SetInputData(ds);
+            _streamTracer->SetInputConnection(_mesher->GetOutputPort());
+            _mesher->Update();
+            mesherOutput = vtkDelaunay3D::SafeDownCast(_mesher)->GetOutput();
+        }
+        DataSet::getCellSizeRange(mesherOutput, cellSizeRange, &avgSize);
+        _dataScale = avgSize / 8.;
+     } else {
+        // DataSet is NOT a cloud (has cells)
+        _streamTracer->SetInputData(ds);
+    }
+
+    _streamTracer->SetMaximumPropagation(xLen + yLen + zLen);
+    _streamTracer->SetIntegratorTypeToRungeKutta45();
+
+    TRACE("Setting streamlines max length to %g", 
+          _streamTracer->GetMaximumPropagation());
+
+    if (_pdMapper == NULL) {
+        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _pdMapper->ScalarVisibilityOn();
+    }
+    if (_seedMapper == NULL) {
+        _seedMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        _seedMapper->SetResolveCoincidentTopologyToPolygonOffset();
+        _seedMapper->ScalarVisibilityOff();
+    }
+
+    // Set up seed source object
+    setSeedToFilledMesh(200);
+
+    switch (_lineType) {
+    case LINES: {
+        _streamTracer->SetComputeVorticity(false);
+        _pdMapper->SetInputConnection(_streamTracer->GetOutputPort());
+    }
+        break;
+    case TUBES: {
+        _streamTracer->SetComputeVorticity(true);
+        _lineFilter = vtkSmartPointer<vtkTubeFilter>::New();
+        vtkTubeFilter *tubeFilter = vtkTubeFilter::SafeDownCast(_lineFilter);
+        tubeFilter->SetNumberOfSides(5);
+        _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
+        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
+    }
+        break;
+    case RIBBONS: {
+        _streamTracer->SetComputeVorticity(true);
+        _lineFilter = vtkSmartPointer<vtkRibbonFilter>::New();
+        _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
+        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
+    }
+        break;
+    default:
+        ERROR("Unknown LineType: %d", _lineType);
+    }
+
+#if 0 && defined(WANT_TRACE)
+    _streamTracer->Update();
+    vtkPolyData *pd = _streamTracer->GetOutput();
+    DataSet::print(pd);
+    TRACE("Points: %d Verts: %d Lines: %d Polys: %d Strips: %d",
+          pd->GetNumberOfPoints(),
+          pd->GetNumberOfVerts(),
+          pd->GetNumberOfLines(),
+          pd->GetNumberOfPolys(),
+          pd->GetNumberOfStrips());
+#if 0
+    vtkCellArray *arr = pd->GetLines();
+    arr->InitTraversal();
+    vtkIdType npts, *pts;
+    arr->GetNextCell(npts, pts);
+    for (int i = 0; i < npts; i++) {
+        TRACE("Pt: %d", pts[i]);
+    }
+#endif
+#endif
+
+    initProp();
+
+    _seedActor->SetMapper(_seedMapper);
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+    }
+
+    setColorMode(_colorMode);
+
+    _linesActor->SetMapper(_pdMapper);
+    _pdMapper->Update();
+    _seedMapper->Update();
+#ifdef WANT_TRACE
+    double *b = getBounds();
+    TRACE("bounds: %g %g %g %g %g %g", b[0], b[1], b[2], b[3], b[4], b[5]);
+#endif
+}
+
+void Streamlines::setNumberOfSeedPoints(int numPoints)
+{
+    switch(_seedType) {
+    case DATASET_FILLED_MESH:
+        setSeedToFilledMesh(numPoints);
+        break;
+    case FILLED_MESH:
+        if (_seedMesh != NULL) {
+            setSeedToFilledMesh(_seedMesh, numPoints);
+        } else {
+            ERROR("NULL _seedMesh");
+        }
+        break;
+    case DATASET_MESH_POINTS:
+        setSeedToMeshPoints(numPoints);
+        break;
+    case MESH_POINTS:
+        setSeedToMeshPoints(_seedMesh, numPoints);
+        break;
+    default:
+        ERROR("Can't set number of points for seed type %d", _seedType);
+        break;
+    }
+    _pdMapper->Update();
+    _seedMapper->Update();
+}
+
+/**
+ * \brief Use points of the DataSet associated with this 
+ * Streamlines as seeds
+ */
+void Streamlines::setSeedToMeshPoints(int maxPoints)
+{
+    _seedType = DATASET_MESH_POINTS;
+    setSeedToMeshPoints(_dataSet->getVtkDataSet(), maxPoints);
+}
+
+/**
+ * \brief Use seed points randomly distributed within the cells 
+ * of the DataSet associated with this Streamlines
+ *
+ * Note: The current implementation doesn't give a uniform 
+ * distribution of points, and points outside the mesh bounds
+ * may be generated
+ *
+ * \param[in] numPoints Number of random seed points to generate
+ */
+void Streamlines::setSeedToFilledMesh(int numPoints)
+{
+    _seedType = DATASET_FILLED_MESH;
+    setSeedToFilledMesh(_dataSet->getVtkDataSet(), numPoints);
+}
+
+/**
+ * \brief Use points of a supplied vtkDataSet as seeds
+ *
+ * \param[in] seed vtkDataSet with points to use as seeds
+ * \param[in] maxPoints Maximum number of points to be used as seeds
+ */
+void Streamlines::setSeedToMeshPoints(vtkDataSet *seed, int maxPoints)
+{
+    if (seed != _dataSet->getVtkDataSet()) {
+        _seedType = MESH_POINTS;
+        _seedMesh = seed;
+    } else {
+        _seedType = DATASET_MESH_POINTS;
+        _seedMesh = NULL;
+    }
+
+    if (_streamTracer == NULL)
+        return;
+
+    if (maxPoints > 0 && seed->GetNumberOfPoints() > maxPoints) {
+        TRACE("Seed points: %d", maxPoints);
+        vtkSmartPointer<vtkMaskPoints> mask = vtkSmartPointer<vtkMaskPoints>::New();
+        mask->SetInputData(seed);
+        mask->SetMaximumNumberOfPoints(maxPoints);
+        mask->RandomModeOn();
+        mask->GenerateVerticesOff();
+        _streamTracer->SetSourceConnection(mask->GetOutputPort());
+
+        vtkSmartPointer<vtkVertexGlyphFilter> vertFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+        vertFilter->SetInputConnection(mask->GetOutputPort());
+        _seedMapper->SetInputConnection(vertFilter->GetOutputPort());
+    } else {
+        TRACE("Seed points: %d", seed->GetNumberOfPoints());
+
+        _streamTracer->SetSourceData(seed);
+        if (vtkPolyData::SafeDownCast(seed) != NULL) {
+            _seedMapper->SetInputData(vtkPolyData::SafeDownCast(seed));
+        } else {
+            vtkSmartPointer<vtkVertexGlyphFilter> vertFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+            vertFilter->SetInputData(seed);
+            _seedMapper->SetInputConnection(vertFilter->GetOutputPort());
+        }
+    }
+}
+
+/**
+ * \brief Use seed points randomly distributed within the cells 
+ * of a supplied vtkDataSet
+ *
+ * Note: The current implementation doesn't give a uniform 
+ * distribution of points, and points outside the mesh bounds
+ * may be generated
+ *
+ * \param[in] ds vtkDataSet containing cells
+ * \param[in] numPoints Number of random seed points to generate
+ */
+void Streamlines::setSeedToFilledMesh(vtkDataSet *ds, int numPoints)
+{
+    if (ds != _dataSet->getVtkDataSet()) {
+        _seedMesh = ds;
+        _seedType = FILLED_MESH;
+#ifdef DEBUG
+        DataSet::print(ds);
+#endif
+    } else {
+        _seedMesh = NULL;
+        if (_mesher != NULL) {
+            if (vtkDelaunay2D::SafeDownCast(_mesher) != NULL) {
+                ds = vtkDelaunay2D::SafeDownCast(_mesher)->GetOutput();
+            } else {
+                ds = vtkDelaunay3D::SafeDownCast(_mesher)->GetOutput();
+            }
+        }
+    }
+    if (_streamTracer != NULL) {
+        // Set up seed source object
+        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
+        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
+
+        if (ds->GetNumberOfCells() < 1) {
+            ERROR("No cells in mesh");
+        }
+
+        for (int i = 0; i < numPoints; i++) {
+            double pt[3];
+            getRandomCellPt(pt, ds);
+            //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
+            pts->InsertNextPoint(pt);
+            cells->InsertNextCell(1);
+            cells->InsertCellPoint(i);
+        }
+
+        seed->SetPoints(pts);
+        seed->SetVerts(cells);
+
+        TRACE("Seed points: %d", seed->GetNumberOfPoints());
+        vtkSmartPointer<vtkDataSet> oldSeed;
+        if (_streamTracer->GetSource() != NULL) {
+            oldSeed = _streamTracer->GetSource();
+        }
+
+        _streamTracer->SetSourceData(seed);
+        _seedMapper->SetInputData(seed);
+    }
+}
+
+/**
+ * \brief Use seed points along a line
+ *
+ * \param[in] start Starting point of rake line
+ * \param[in] end End point of rake line
+ * \param[in] numPoints Number of points along line to generate
+ */
+void Streamlines::setSeedToRake(double start[3], double end[3], int numPoints)
+{
+    if (numPoints < 2)
+        return;
+    _seedType = RAKE;
+    _seedMesh = NULL;
+    if (_streamTracer != NULL) {
+        // Set up seed source object
+        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
+        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
+        vtkSmartPointer<vtkPolyLine> polyline = vtkSmartPointer<vtkPolyLine>::New();
+
+        double dir[3];
+        for (int i = 0; i < 3; i++) {
+            dir[i] = end[i] - start[i];
+        }
+
+        polyline->GetPointIds()->SetNumberOfIds(numPoints);
+        for (int i = 0; i < numPoints; i++) {
+            double pt[3];
+            for (int ii = 0; ii < 3; ii++) {
+                pt[ii] = start[ii] + dir[ii] * ((double)i / (numPoints-1));
+            }
+            //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
+            pts->InsertNextPoint(pt);
+            polyline->GetPointIds()->SetId(i, i);
+        }
+
+        cells->InsertNextCell(polyline);
+        seed->SetPoints(pts);
+        seed->SetLines(cells);
+
+        TRACE("Seed points: %d", seed->GetNumberOfPoints());
+        vtkSmartPointer<vtkDataSet> oldSeed;
+        if (_streamTracer->GetSource() != NULL) {
+            oldSeed = _streamTracer->GetSource();
+        }
+        _streamTracer->SetSourceData(seed);
+        _seedMapper->SetInputData(seed);
+    }
+}
+
+/**
+ * \brief Create seed points inside a disk with an optional hole
+ *
+ * \param[in] center Center point of disk
+ * \param[in] normal Normal vector to orient disk
+ * \param[in] radius Radius of disk
+ * \param[in] innerRadius Radius of hole at center of disk
+ * \param[in] numPoints Number of random points to generate
+ */
+void Streamlines::setSeedToDisk(double center[3],
+                                double normal[3],
+                                double radius,
+                                double innerRadius,
+                                int numPoints)
+{
+    _seedType = DISK;
+    _seedMesh = NULL;
+    if (_streamTracer != NULL) {
+        // Set up seed source object
+        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
+        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
+
+        // The following code is based on vtkRegularPolygonSource::RequestData
+
+        double px[3];
+        double py[3];
+        double axis[3] = {1., 0., 0.};
+
+        if (vtkMath::Normalize(normal) == 0.0) {
+            normal[0] = 0.0;
+            normal[1] = 0.0;
+            normal[2] = 1.0;
+        }
+
+        // Find axis in plane (orthogonal to normal)
+        bool done = false;
+        vtkMath::Cross(normal, axis, px);
+        if (vtkMath::Normalize(px) > 1.0e-3) {
+            done = true;
+        }
+        if (!done) {
+            axis[0] = 0.0;
+            axis[1] = 1.0;
+            axis[2] = 0.0;
+            vtkMath::Cross(normal, axis, px);
+            if (vtkMath::Normalize(px) > 1.0e-3) {
+                done = true;
+            }
+        }
+        if (!done) {
+            axis[0] = 0.0;
+            axis[1] = 0.0;
+            axis[2] = 1.0;
+            vtkMath::Cross(normal, axis, px);
+            vtkMath::Normalize(px);
+        }
+        // Create third orthogonal basis vector
+        vtkMath::Cross(px, normal, py);
+
+        double minSquared = (innerRadius*innerRadius)/(radius*radius);
+        for (int j = 0; j < numPoints; j++) {
+            // Get random sweep angle and radius
+            double angle = getRandomNum(0, 2.0 * vtkMath::Pi());
+            // Need sqrt to get uniform distribution
+            double r = sqrt(getRandomNum(minSquared, 1)) * radius;
+            double pt[3];
+            for (int i = 0; i < 3; i++) {
+                pt[i] = center[i] + r * (px[i] * cos(angle) + py[i] * sin(angle));
+            }
+            //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
+            pts->InsertNextPoint(pt);
+            cells->InsertNextCell(1);
+            cells->InsertCellPoint(j);
+        }
+
+        seed->SetPoints(pts);
+        seed->SetVerts(cells);
+
+        TRACE("Seed points: %d", seed->GetNumberOfPoints());
+        vtkSmartPointer<vtkDataSet> oldSeed;
+        if (_streamTracer->GetSource() != NULL) {
+            oldSeed = _streamTracer->GetSource();
+        }
+        _streamTracer->SetSourceData(seed);
+        _seedMapper->SetInputData(seed);
+    }
+}
+
+/**
+ * \brief Use seed points from an n-sided polygon
+ *
+ * \param[in] center Center point of polygon
+ * \param[in] normal Normal vector to orient polygon
+ * \param[in] angle Angle in degrees to rotate about normal
+ * \param[in] radius Radius of circumscribing circle
+ * \param[in] numSides Number of polygon sides (and points) to generate
+ */
+void Streamlines::setSeedToPolygon(double center[3],
+                                   double normal[3],
+                                   double angle,
+                                   double radius,
+                                   int numSides)
+{
+    _seedType = POLYGON;
+    _seedMesh = NULL;
+    if (_streamTracer != NULL) {
+        // Set up seed source object
+        vtkSmartPointer<vtkRegularPolygonSource> seed = vtkSmartPointer<vtkRegularPolygonSource>::New();
+
+        seed->SetCenter(center);
+        seed->SetNormal(normal);
+        seed->SetRadius(radius);
+        seed->SetNumberOfSides(numSides);
+        seed->GeneratePolygonOn();
+
+        if (angle != 0.0) {
+            vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+            trans->RotateWXYZ(angle, normal);
+            vtkSmartPointer<vtkTransformPolyDataFilter> transFilt =
+                vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+            transFilt->SetInputConnection(seed->GetOutputPort());
+            transFilt->SetTransform(trans);
+        }
+
+        TRACE("Seed points: %d", numSides);
+        vtkSmartPointer<vtkDataSet> oldSeed;
+        if (_streamTracer->GetSource() != NULL) {
+            oldSeed = _streamTracer->GetSource();
+        }
+
+        if (angle != 0.0) {
+            vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+            trans->Translate(+center[0], +center[1], +center[2]);
+            trans->RotateWXYZ(angle, normal);
+            trans->Translate(-center[0], -center[1], -center[2]);
+            vtkSmartPointer<vtkTransformPolyDataFilter> transFilt =
+                vtkSmartPointer<vtkTransformPolyDataFilter>::New();
+            transFilt->SetInputConnection(seed->GetOutputPort());
+            transFilt->SetTransform(trans);
+            _streamTracer->SetSourceConnection(transFilt->GetOutputPort());
+            _seedMapper->SetInputConnection(transFilt->GetOutputPort());
+        } else {
+            _streamTracer->SetSourceConnection(seed->GetOutputPort());
+            _seedMapper->SetInputConnection(seed->GetOutputPort());
+        }
+    }
+}
+
+/**
+ * \brief Use seed points from an n-sided polygon
+ *
+ * \param[in] center Center point of polygon
+ * \param[in] normal Normal vector to orient polygon
+ * \param[in] angle Angle in degrees to rotate about normal
+ * \param[in] radius Radius of circumscribing circle
+ * \param[in] numSides Number of polygon sides (and points) to generate
+ * \param[in] numPoints Number of random points to generate
+ */
+void Streamlines::setSeedToFilledPolygon(double center[3],
+                                         double normal[3],
+                                         double angle,
+                                         double radius,
+                                         int numSides,
+                                         int numPoints)
+{
+    _seedType = FILLED_POLYGON;
+    _seedMesh = NULL;
+    if (_streamTracer != NULL) {
+         // Set up seed source object
+        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
+        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
+
+        // The following code is based on vtkRegularPolygonSource::RequestData
+
+        double px[3];
+        double py[3];
+        double axis[3] = {1., 0., 0.};
+
+        if (vtkMath::Normalize(normal) == 0.0) {
+            normal[0] = 0.0;
+            normal[1] = 0.0;
+            normal[2] = 1.0;
+        }
+
+        // Find axis in plane (orthogonal to normal)
+        bool done = false;
+        vtkMath::Cross(normal, axis, px);
+        if (vtkMath::Normalize(px) > 1.0e-3) {
+            done = true;
+        }
+        if (!done) {
+            axis[0] = 0.0;
+            axis[1] = 1.0;
+            axis[2] = 0.0;
+            vtkMath::Cross(normal, axis, px);
+            if (vtkMath::Normalize(px) > 1.0e-3) {
+                done = true;
+            }
+        }
+        if (!done) {
+            axis[0] = 0.0;
+            axis[1] = 0.0;
+            axis[2] = 1.0;
+            vtkMath::Cross(normal, axis, px);
+            vtkMath::Normalize(px);
+        }
+        // Create third orthogonal basis vector
+        vtkMath::Cross(px, normal, py);
+
+        double verts[numSides][3];
+        double sliceTheta = 2.0 * vtkMath::Pi() / (double)numSides;
+        angle = vtkMath::RadiansFromDegrees(angle);
+        for (int j = 0; j < numSides; j++) {
+            for (int i = 0; i < 3; i++) {
+                double theta = sliceTheta * (double)j - angle;
+                verts[j][i] = center[i] + radius * (px[i] * cos(theta) + 
+                                                    py[i] * sin(theta));
+            }
+            //TRACE("Vert %d: %g %g %g", j, verts[j][0], verts[j][1], verts[j][2]);
+        }
+
+        // Note: this gives a uniform distribution because the polygon is regular and
+        // the triangular sections have equal area
+        if (numSides == 3) {
+            for (int j = 0; j < numPoints; j++) {
+                double pt[3];
+                getRandomPointInTriangle(pt, verts[0], verts[1], verts[2]);
+                //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
+                pts->InsertNextPoint(pt);
+                cells->InsertNextCell(1);
+                cells->InsertCellPoint(j);
+            }
+        } else {
+            for (int j = 0; j < numPoints; j++) {
+                // Get random triangle section
+                int tri = rand() % numSides;
+                double pt[3];
+                getRandomPointInTriangle(pt, center, verts[tri], verts[(tri+1) % numSides]);
+                //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
+                pts->InsertNextPoint(pt);
+                cells->InsertNextCell(1);
+                cells->InsertCellPoint(j);
+            }
+        }
+
+        seed->SetPoints(pts);
+        seed->SetVerts(cells);
+
+        TRACE("Seed points: %d", seed->GetNumberOfPoints());
+        vtkSmartPointer<vtkDataSet> oldSeed;
+        if (_streamTracer->GetSource() != NULL) {
+            oldSeed = _streamTracer->GetSource();
+        }
+
+        _streamTracer->SetSourceData(seed);
+        _seedMapper->SetInputData(seed);
+    }
+}
+
+/**
+ * \brief Set the integration method used
+ */
+void Streamlines::setIntegrator(IntegratorType integrator)
+{
+    if (_streamTracer != NULL) {
+        switch (integrator) {
+        case RUNGE_KUTTA2:
+            _streamTracer->SetIntegratorTypeToRungeKutta2();
+            break;
+        case RUNGE_KUTTA4:
+            _streamTracer->SetIntegratorTypeToRungeKutta4();
+            break;
+        case RUNGE_KUTTA45:
+            _streamTracer->SetIntegratorTypeToRungeKutta45();
+            break;
+        default:
+            ;
+        }
+    }
+}
+
+/**
+ * \brief Set the direction of integration
+ */
+void Streamlines::setIntegrationDirection(IntegrationDirection dir)
+{
+    if (_streamTracer != NULL) {
+        switch (dir) {
+        case FORWARD:
+            _streamTracer->SetIntegrationDirectionToForward();
+            break;
+        case BACKWARD:
+            _streamTracer->SetIntegrationDirectionToBackward();
+            break;
+        case BOTH:
+            _streamTracer->SetIntegrationDirectionToBoth();
+            break;
+        default:
+            ;
+        }
+    }
+}
+
+/**
+ * \brief Set the step size units.  Length units are world 
+ * coordinates, and cell units means steps are from cell to 
+ * cell.  Default is cell units.
+ *
+ * Note: calling this function will not convert existing
+ * initial, minimum or maximum step value settings to the
+ * new units, so this function should be called before 
+ * setting step values.
+ */
+void Streamlines::setIntegrationStepUnit(StepUnit unit)
+{
+    if (_streamTracer != NULL) {
+        switch (unit) {
+        case LENGTH_UNIT:
+            _streamTracer->SetIntegrationStepUnit(vtkStreamTracer::LENGTH_UNIT);
+            break;
+        case CELL_LENGTH_UNIT:
+            _streamTracer->SetIntegrationStepUnit(vtkStreamTracer::CELL_LENGTH_UNIT);
+            break;
+        default:
+            ;
+        }
+    }
+}
+
+/**
+ * \brief Set initial step size for adaptive step integrator in 
+ * step units (see setIntegrationStepUnit).  For non-adaptive
+ * integrators, this is the fixed step size.
+ */
+void Streamlines::setInitialIntegrationStep(double step)
+{
+    if (_streamTracer != NULL) {
+        _streamTracer->SetInitialIntegrationStep(step);
+    }
+}
+
+/**
+ * \brief Set minimum step for adaptive step integrator in 
+ * step units (see setIntegrationStepUnit)
+ */
+void Streamlines::setMinimumIntegrationStep(double step)
+{
+    if (_streamTracer != NULL) {
+        _streamTracer->SetMinimumIntegrationStep(step);
+    }
+}
+
+/**
+ * \brief Set maximum step for adaptive step integrator in 
+ * step units (see setIntegrationStepUnit)
+ */
+void Streamlines::setMaximumIntegrationStep(double step)
+{
+    if (_streamTracer != NULL) {
+        _streamTracer->SetMaximumIntegrationStep(step);
+    }
+}
+
+/**
+ * \brief Set maximum error tolerance
+ */
+void Streamlines::setMaximumError(double error)
+{
+    if (_streamTracer != NULL) {
+        _streamTracer->SetMaximumError(error);
+    }
+}
+
+/**
+ * \brief Set maximum length of stream lines in world
+ * coordinates
+ */
+void Streamlines::setMaxPropagation(double length)
+{
+    if (_streamTracer != NULL) {
+        _streamTracer->SetMaximumPropagation(length);
+    }
+}
+
+/**
+ * \brief Set maximum number of integration steps
+ */
+void Streamlines::setMaxNumberOfSteps(int steps)
+{
+    if (_streamTracer != NULL) {
+        _streamTracer->SetMaximumNumberOfSteps(steps);
+    }
+}
+
+/**
+ * \brief Set the minimum speed before integration stops
+ */
+void Streamlines::setTerminalSpeed(double speed)
+{
+    if (_streamTracer != NULL) {
+        _streamTracer->SetTerminalSpeed(speed);
+    }
+}
+
+/**
+ * \brief Set streamline type to polylines
+ */
+void Streamlines::setLineTypeToLines()
+{
+    _lineType = LINES;
+    if (_streamTracer != NULL &&
+        _pdMapper != NULL) {
+        _streamTracer->SetComputeVorticity(false);
+        _pdMapper->SetInputConnection(_streamTracer->GetOutputPort());
+        _lineFilter = NULL;
+        setCulling(_linesActor->GetProperty(), false);
+        _linesActor->GetProperty()->SetRepresentationToWireframe();
+        _linesActor->GetProperty()->LightingOff();
+    }
+}
+
+/**
+ * \brief Set streamline type to 3D tubes
+ *
+ * \param[in] numSides Number of sides (>=3) for tubes
+ * \param[in] radius World coordinate minimum tube radius
+ */
+void Streamlines::setLineTypeToTubes(int numSides, double radius)
+{
+    _lineType = TUBES;
+    if (_streamTracer != NULL) {
+        _streamTracer->SetComputeVorticity(true);
+        if (vtkTubeFilter::SafeDownCast(_lineFilter) == NULL) {
+            _lineFilter = vtkSmartPointer<vtkTubeFilter>::New();
+            _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
+        }
+        vtkTubeFilter *tubeFilter = vtkTubeFilter::SafeDownCast(_lineFilter);
+        if (numSides < 3)
+            numSides = 3;
+        tubeFilter->SetNumberOfSides(numSides);
+        tubeFilter->SetRadius(_dataScale * radius);
+        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
+        if (_faceCulling && _opacity == 1.0)
+            setCulling(_linesActor->GetProperty(), true);
+        _linesActor->GetProperty()->SetRepresentationToSurface();
+        _linesActor->GetProperty()->LightingOn();
+     }
+}
+
+/**
+ * \brief Set streamline type to 3D ribbons
+ *
+ * \param[in] width Minimum half-width of ribbons
+ * \param[in] angle Default ribbon angle in degrees from normal
+ */
+void Streamlines::setLineTypeToRibbons(double width, double angle)
+{
+    _lineType = RIBBONS;
+    if (_streamTracer != NULL) {
+        _streamTracer->SetComputeVorticity(true);
+        if (vtkRibbonFilter::SafeDownCast(_lineFilter) == NULL) {
+            _lineFilter = vtkSmartPointer<vtkRibbonFilter>::New();
+            _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
+        }
+        vtkRibbonFilter *ribbonFilter = vtkRibbonFilter::SafeDownCast(_lineFilter);
+        ribbonFilter->SetWidth(_dataScale * width);
+        ribbonFilter->SetAngle(angle);
+        ribbonFilter->UseDefaultNormalOn();
+        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
+        setCulling(_linesActor->GetProperty(), false);
+        _linesActor->GetProperty()->SetRepresentationToSurface();
+        _linesActor->GetProperty()->LightingOn();
+    }
+}
+
+void Streamlines::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                         _dataSet->getActiveVectorsName(),
+                                         3);
+        for (int i = 0; i < 3; i++) {
+            renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                             _dataSet->getActiveVectorsName(),
+                                             3, i);
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+
+    // Need to update color map ranges and/or active vector field
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+}
+
+void Streamlines::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void Streamlines::setColorMode(ColorMode mode,
+                               const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void Streamlines::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                               const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _pdMapper == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        _pdMapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        _pdMapper->SetScalarModeToUseCellFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (name != NULL && strlen(name) > 0) {
+        _pdMapper->SelectColorArray(name);
+    } else {
+        _pdMapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _pdMapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _pdMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _pdMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _pdMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _pdMapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _pdMapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void Streamlines::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Streamlines::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_pdMapper != NULL) {
+            _pdMapper->UseLookupTableScalarRangeOn();
+            _pdMapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Turn on/off lighting of this object
+ */
+void Streamlines::setLighting(bool state)
+{
+    _lighting = state;
+    if (_linesActor != NULL)
+        _linesActor->GetProperty()->SetLighting((state ? 1 : 0));
+}
+
+/**
+ * \brief Set opacity of this object
+ */
+void Streamlines::setOpacity(double opacity)
+{
+    _opacity = opacity;
+    if (_linesActor != NULL) {
+        _linesActor->GetProperty()->SetOpacity(_opacity);
+        if (_opacity < 1.0)
+            setCulling(_linesActor->GetProperty(), false);
+        else if (_faceCulling && _lineType == TUBES)
+            setCulling(_linesActor->GetProperty(), true);
+    }
+    if (_seedActor != NULL) {
+        _seedActor->GetProperty()->SetOpacity(_opacity);
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of this Streamlines
+ */
+void Streamlines::setVisibility(bool state)
+{
+    if (_linesActor != NULL) {
+        _linesActor->SetVisibility((state ? 1 : 0));
+    }
+    if (_seedActor != NULL) {
+        if (!state ||
+            (state && _seedVisible)) {
+            _seedActor->SetVisibility((state ? 1 : 0));
+        }
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of the seed geometry
+ */
+void Streamlines::setSeedVisibility(bool state)
+{
+    _seedVisible = state;
+    if (_seedActor != NULL) {
+        _seedActor->SetVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Get visibility state of the Streamlines
+ * 
+ * \return Are the Streamlines visible?
+ */
+bool Streamlines::getVisibility()
+{
+    if (_linesActor == NULL) {
+        return false;
+    } else {
+        return (_linesActor->GetVisibility() != 0);
+    }
+}
+
+/**
+ * \brief Turn on/off rendering of edges
+ */
+void Streamlines::setEdgeVisibility(bool state)
+{
+    if (_linesActor != NULL) {
+        _linesActor->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
+    }
+}
+
+/**
+ * \brief Set RGB color of stream lines
+ */
+void Streamlines::setColor(float color[3])
+{
+    _color[0] = color[0];
+    _color[1] = color[1];
+    _color[2] = color[2];
+    if (_linesActor != NULL)
+        _linesActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
+}
+
+/**
+ * \brief Set RGB color of stream line edges
+ */
+void Streamlines::setEdgeColor(float color[3])
+{
+    _edgeColor[0] = color[0];
+    _edgeColor[1] = color[1];
+    _edgeColor[2] = color[2];
+    if (_linesActor != NULL)
+        _linesActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
+}
+
+/**
+ * \brief Set RGB color of seed geometry
+ */
+void Streamlines::setSeedColor(float color[3])
+{
+    _seedColor[0] = color[0];
+    _seedColor[1] = color[1];
+    _seedColor[2] = color[2];
+    if (_seedActor != NULL) {
+        _seedActor->GetProperty()->SetColor(_seedColor[0], _seedColor[1], _seedColor[2]);
+        _seedActor->GetProperty()->SetEdgeColor(_seedColor[0], _seedColor[1], _seedColor[2]);
+    }
+}
+
+/**
+ * \brief Set point size of seed geometry (may be a no-op)
+ */
+void Streamlines::setSeedPointSize(float size)
+{
+    if (_seedActor != NULL) {
+        _seedActor->GetProperty()->SetPointSize(size);
+    }
+}
+
+/**
+ * \brief Set pixel width of stream lines (may be a no-op)
+ */
+void Streamlines::setEdgeWidth(float edgeWidth)
+{
+    _edgeWidth = edgeWidth;
+    if (_linesActor != NULL)
+        _linesActor->GetProperty()->SetLineWidth(_edgeWidth);
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Streamlines::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_pdMapper != NULL) {
+        _pdMapper->SetClippingPlanes(planes);
+    }
+    if (_seedMapper != NULL) {
+        _seedMapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Streamlines.h
===================================================================
--- vtkvis/tags/1.8.4/Streamlines.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Streamlines.h	(revision 6184)
@@ -0,0 +1,224 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_STREAMLINES_H
+#define VTKVIS_STREAMLINES_H
+
+#include <vtkSmartPointer.h>
+#include <vtkProp.h>
+#include <vtkPlaneCollection.h>
+#include <vtkStreamTracer.h>
+#include <vtkPolyDataAlgorithm.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkLookupTable.h>
+#include <vtkAssembly.h>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Streamline visualization of vector fields
+ *
+ * The DataSet must contain vectors
+ */
+class Streamlines : public GraphicsObject {
+public:
+    enum LineType {
+        LINES,
+        TUBES,
+        RIBBONS
+    };
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+    enum StepUnit {
+        LENGTH_UNIT,
+        CELL_LENGTH_UNIT
+    };
+    enum IntegratorType {
+        RUNGE_KUTTA2,
+        RUNGE_KUTTA4,
+        RUNGE_KUTTA45
+    };
+    enum IntegrationDirection {
+        FORWARD,
+        BACKWARD,
+        BOTH
+    };
+    enum SeedType {
+        DATASET_MESH_POINTS,
+        DATASET_FILLED_MESH,
+        MESH_POINTS,
+        FILLED_MESH,
+        RAKE,
+        DISK,
+        POLYGON,
+        FILLED_POLYGON
+    };
+
+    Streamlines();
+    virtual ~Streamlines();
+
+    virtual const char *getClassName() const 
+    {
+        return "Streamlines";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setLighting(bool state);
+
+    virtual void setOpacity(double opacity);
+
+    virtual void setVisibility(bool state);
+
+    virtual bool getVisibility();
+
+    virtual void setColor(float color[3]);
+
+    virtual void setEdgeVisibility(bool state);
+
+    virtual void setEdgeColor(float color[3]);
+
+    virtual void setEdgeWidth(float edgeWidth);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setNumberOfSeedPoints(int numPoints);
+
+    void setSeedToMeshPoints(int maxPoints = 500);
+
+    void setSeedToFilledMesh(int numPoints);
+
+    void setSeedToMeshPoints(vtkDataSet *ds, int maxPoints = 500);
+
+    void setSeedToFilledMesh(vtkDataSet *ds, int numPoints);
+
+    void setSeedToRake(double start[3], double end[3], int numPoints);
+
+    void setSeedToDisk(double center[3], double normal[3],
+                       double radius, double innerRadius, int numPoints);
+
+    void setSeedToPolygon(double center[3], double normal[3],
+                          double angle, double radius,
+                          int numSides);
+
+    void setSeedToFilledPolygon(double center[3], double normal[3],
+                                double angle, double radius,
+                                int numSides, int numPoints);
+
+    void setIntegrator(IntegratorType integrator);
+
+    void setIntegrationDirection(IntegrationDirection dir);
+
+    void setIntegrationStepUnit(StepUnit unit);
+
+    void setInitialIntegrationStep(double step);
+
+    void setMinimumIntegrationStep(double step);
+
+    void setMaximumIntegrationStep(double step);
+
+    void setMaximumError(double error);
+
+    void setMaxPropagation(double length);
+
+    void setMaxNumberOfSteps(int steps);
+
+    void setTerminalSpeed(double speed);
+
+    void setLineTypeToLines();
+
+    void setLineTypeToTubes(int numSides, double radius);
+
+    void setLineTypeToRibbons(double width, double angle);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+    void setSeedVisibility(bool state);
+
+    void setSeedColor(float color[3]);
+
+    void setSeedPointSize(float size);
+
+private:
+    virtual void initProp();
+    virtual void update();
+
+    static double getRandomNum(double min, double max);
+    static void getRandomPoint(double pt[3], const double bounds[6]);
+    static void getRandomPointInTriangle(double pt[3],
+                                         const double v0[3],
+                                         const double v1[3],
+                                         const double v2[3]);
+    static void getRandomPointOnLineSegment(double pt[3],
+                                            const double endpt[3],
+                                            const double endpt2[3]);
+    static void getRandomPointInTetrahedron(double pt[3],
+                                            const double v0[3],
+                                            const double v1[3],
+                                            const double v2[3],
+                                            const double v3[3]);
+    static void getRandomCellPt(double pt[3], vtkDataSet *ds);
+
+    LineType _lineType;
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    SeedType _seedType;
+    float _seedColor[3];
+    bool _seedVisible;
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+    double _dataScale;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkActor> _linesActor;
+    vtkSmartPointer<vtkActor> _seedActor;
+    vtkSmartPointer<vtkDataSet> _seedMesh;
+    vtkSmartPointer<vtkAlgorithm> _mesher;
+    vtkSmartPointer<vtkStreamTracer> _streamTracer;
+    vtkSmartPointer<vtkPolyDataAlgorithm> _lineFilter;
+    vtkSmartPointer<vtkPolyDataMapper> _pdMapper;
+    vtkSmartPointer<vtkPolyDataMapper> _seedMapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/TGAWriter.cpp
===================================================================
--- vtkvis/tags/1.8.4/TGAWriter.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/TGAWriter.cpp	(revision 6184)
@@ -0,0 +1,205 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+#include <sys/uio.h>
+
+#include <iostream>
+#include <fstream>
+
+#include "TGAWriter.h"
+#include "Trace.h"
+#ifdef USE_THREADS
+#include "ResponseQueue.h"
+#endif
+
+#ifdef USE_THREADS
+
+/**
+ * \brief Writes image command + data to supplied file descriptor.
+ *
+ * The image data must be supplied in BGR(A) order with bottom to 
+ * top scanline ordering.
+ *
+ * \param[in] queue Pointer to ResponseQueue to write to
+ * \param[in] cmdName Command name to send (byte length will be appended)
+ * \param[in] data Image data
+ * \param[in] width Width of image in pixels
+ * \param[in] height Height of image in pixels
+ * \param[in] bytesPerPixel Should be 3 or 4, depending on alpha
+ */
+void
+VtkVis::queueTGA(ResponseQueue *queue, const char *cmdName, 
+                 const unsigned char *data,
+                 int width, int height,
+                 int bytesPerPixel)
+{
+    TRACE("(%dx%d)\n", width, height);
+
+    size_t headerLength = 18;
+
+    char header[headerLength];
+    memset(header, 0, headerLength);
+    header[2] = (char)2;  // image type (2 = uncompressed true-color)
+    header[12] = (char)width;
+    header[13] = (char)(width >> 8);
+    header[14] = (char)height;
+    header[15] = (char)(height >> 8);
+    header[16] = (char)(bytesPerPixel*8); // bits per pixel
+
+    size_t dataLength = width * height * bytesPerPixel;
+    size_t cmdLength;
+
+    char command[200];
+    cmdLength = snprintf(command, sizeof(command), "%s %lu\n", cmdName, 
+                         (unsigned long)headerLength + dataLength);
+
+    size_t length;
+    unsigned char *mesg = NULL;
+
+    length = headerLength + dataLength + cmdLength;
+    mesg = (unsigned char *)malloc(length);
+    if (mesg == NULL) {
+        ERROR("can't allocate %ld bytes for the image message", length);
+        return;
+    }
+    memcpy(mesg, command, cmdLength);
+    memcpy(mesg + cmdLength, header, headerLength);
+    memcpy(mesg + cmdLength + headerLength, data, dataLength);
+
+    Response *response = NULL;
+    if (strncmp(cmdName, "nv>legend", 9) == 0) {
+        response = new Response(Response::LEGEND);
+    } else {
+        response = new Response(Response::IMAGE);
+    }
+    response->setMessage(mesg, length, Response::DYNAMIC);
+    queue->enqueue(response);
+    TRACE("Leaving (%dx%d)\n", width, height);
+}
+#else
+
+/**
+ * \brief Writes image command + data to supplied file descriptor.
+ *
+ * The image data must be supplied in BGR(A) order with bottom to 
+ * top scanline ordering.
+ *
+ * \param[in] fd File descriptor that will be written to
+ * \param[in] cmdName Command name to send (byte length will be appended)
+ * \param[in] data Image data
+ * \param[in] width Width of image in pixels
+ * \param[in] height Height of image in pixels
+ * \param[in] bytesPerPixel Should be 3 or 4, depending on alpha
+ */
+void
+VtkVis::writeTGA(int fd, const char *cmdName,
+                 const unsigned char *data,
+                 int width, int height,
+                 int bytesPerPixel)
+{
+    TRACE("(%dx%d)\n", width, height);
+
+    size_t headerLength = 18;
+
+    char header[headerLength];
+    memset(header, 0, headerLength);
+    header[2] = (char)2;  // image type (2 = uncompressed true-color)
+    header[12] = (char)width;
+    header[13] = (char)(width >> 8);
+    header[14] = (char)height;
+    header[15] = (char)(height >> 8);
+    header[16] = (char)(bytesPerPixel*8); // bits per pixel
+
+    size_t dataLength = width * height * bytesPerPixel;
+
+    char command[200];
+    snprintf(command, sizeof(command), "%s %lu\n", cmdName, 
+             (unsigned long)headerLength + dataLength);
+
+    size_t nRecs = 3;
+
+    struct iovec *iov;
+    iov = (struct iovec *)malloc(sizeof(struct iovec) * nRecs);
+
+    // Write the command, then the image header and data.
+    // Command
+    iov[0].iov_base = command;
+    iov[0].iov_len = strlen(command);
+    // Header of image data
+    iov[1].iov_base = header;
+    iov[1].iov_len = headerLength;
+    // Image data **must be BGR(A)!**
+    iov[2].iov_base = data;
+    iov[2].iov_len = dataLength;
+
+    if (writev(fd, iov, nRecs) < 0) {
+	ERROR("write failed: %s\n", strerror(errno));
+    }
+    free(iov);
+
+    TRACE("Leaving (%dx%d)\n", width, height);
+}
+#endif  /*USE_THREADS*/
+
+/**
+ * \brief Writes image data to supplied file name
+ *
+ * The image data must be supplied with bottom to top 
+ * scanline ordering.  Source data should have BGR(A) 
+ * ordering, unless srcIsRGB is true, in which case 
+ * the source data will be converted from RGB(A) to 
+ * BGR(A).  Note that this is slow and it is better 
+ * to pass in BGR(A) data.
+ *
+ * \param[in] filename Path to file that will be written
+ * \param[in] imgData Image data
+ * \param[in] width Width of image in pixels
+ * \param[in] height Height of image in pixels
+ * \param[in] bytesPerPixel Should be 3 or 4, depending on alpha
+ * \param[in] srcIsRGB If true source data will be re-ordered
+ */
+void
+VtkVis::writeTGAFile(const char *filename,
+                     const unsigned char *imgData,
+                     int width, int height,
+                     int bytesPerPixel,
+                     bool srcIsRGB)
+{
+    TRACE("%s (%dx%d)\n", filename, width, height);
+
+    std::ofstream outfile(filename, std::ios::out | std::ios::binary | std::ios::trunc);
+    char header[18];
+    memset(header, 0, 18);
+    header[2] = (char)2;  // image type (2 = uncompressed true-color)
+    header[12] = (char)width;
+    header[13] = (char)(width >> 8);
+    header[14] = (char)height;
+    header[15] = (char)(height >> 8);
+    header[16] = (char)(bytesPerPixel*8); // bits per pixel
+
+    outfile.write(header, sizeof(header));
+
+    if (!srcIsRGB) {
+        outfile.write((const char *)imgData, width * height * bytesPerPixel);
+    } else {
+        // RGB(A) -> BGR(A)
+        for (int i = 0; i < width * height; i++) {
+            outfile << imgData[i*bytesPerPixel+2]
+                    << imgData[i*bytesPerPixel+1]
+                    << imgData[i*bytesPerPixel];
+            if (bytesPerPixel == 4) {
+                outfile << imgData[i*bytesPerPixel+3];
+            }
+        }
+    }
+
+    outfile.close();
+}
Index: vtkvis/tags/1.8.4/TGAWriter.h
===================================================================
--- vtkvis/tags/1.8.4/TGAWriter.h	(revision 6184)
+++ vtkvis/tags/1.8.4/TGAWriter.h	(revision 6184)
@@ -0,0 +1,33 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_TGAWRITER_H
+#define VTKVIS_TGAWRITER_H
+
+#ifdef USE_THREADS
+#include "ResponseQueue.h"
+#endif
+
+namespace VtkVis {
+#ifdef USE_THREADS
+extern
+void queueTGA(ResponseQueue *queue, const char *cmdName, 
+              const unsigned char *data, int width, int height, 
+              int bytesPerPixel);
+#else
+extern
+void writeTGA(int fd, const char *cmdName, 
+              const unsigned char *data, int width, int height, 
+              int bytesPerPixel);
+#endif
+extern
+void writeTGAFile(const char *filename, 
+                  const unsigned char *data, int width, int height, 
+                  int bytesPerPixel, bool srcIsRGB = false);
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Text3D.cpp
===================================================================
--- vtkvis/tags/1.8.4/Text3D.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Text3D.cpp	(revision 6184)
@@ -0,0 +1,140 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <vtkSmartPointer.h>
+#include <vtkTextActor3D.h>
+#include <vtkTextProperty.h>
+#include <vtkProp3DFollower.h>
+#include <vtkRenderer.h>
+
+#include "Text3D.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Text3D::Text3D() :
+    GraphicsObject()
+{
+}
+
+Text3D::~Text3D()
+{
+    TRACE("Deleting Text3D");
+}
+
+void Text3D::initProp()
+{
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkTextActor3D>::New();
+        vtkTextProperty *property = getTextProperty();
+        property->SetColor(_color[0], _color[1], _color[2]);
+        if (_dataSet != NULL)
+            _opacity = _dataSet->getOpacity();
+        property->SetOpacity(_opacity);
+
+        if (_dataSet != NULL)
+            setVisibility(_dataSet->getVisibility());
+    }
+}
+
+void Text3D::update()
+{
+    initProp();
+}
+
+void Text3D::setOpacity(double opacity)
+{
+    GraphicsObject::setOpacity(opacity);
+
+    vtkTextProperty *property = getTextProperty();
+    if (property == NULL)
+        return;
+
+    property->SetOpacity(_opacity);
+}
+
+void Text3D::setColor(float color[3])
+{
+    GraphicsObject::setColor(color);
+
+    vtkTextProperty *property = getTextProperty();
+    if (property == NULL)
+        return;
+
+    property->SetColor(_color[0], _color[1], _color[2]);
+}
+
+void Text3D::setFont(const char *fontName)
+{
+    vtkTextProperty *property = getTextProperty();
+    if (property == NULL)
+        return;
+
+    property->SetFontFamilyAsString(fontName);
+}
+
+void Text3D::setFontSize(int size)
+{
+    vtkTextProperty *property = getTextProperty();
+    if (property == NULL)
+        return;
+
+    property->SetFontSize(size);
+}
+
+void Text3D::setBold(bool state)
+{
+    vtkTextProperty *property = getTextProperty();
+    if (property == NULL)
+        return;
+
+    property->SetBold((state ? 1 : 0));
+}
+
+void Text3D::setItalic(bool state)
+{
+    vtkTextProperty *property = getTextProperty();
+    if (property == NULL)
+        return;
+
+    property->SetItalic((state ? 1 : 0));
+}
+
+void Text3D::setShadow(bool state)
+{
+    vtkTextProperty *property = getTextProperty();
+    if (property == NULL)
+        return;
+
+    property->SetShadow((state ? 1 : 0));
+}
+
+void Text3D::setFollowCamera(bool state, vtkRenderer *renderer)
+{
+    if (state && vtkTextActor3D::SafeDownCast(_prop) != NULL) {
+        vtkSmartPointer<vtkTextActor3D> textActor = vtkTextActor3D::SafeDownCast(_prop);
+        vtkSmartPointer<vtkProp3DFollower> follower = vtkSmartPointer<vtkProp3DFollower>::New();
+        follower->SetCamera(renderer->GetActiveCamera());
+        follower->SetProp3D(textActor);
+        renderer->RemoveViewProp(textActor);
+        _prop = follower;
+        renderer->AddViewProp(_prop);
+    } else if (!state && vtkProp3DFollower::SafeDownCast(_prop) != NULL) {
+        vtkSmartPointer<vtkProp3DFollower> follower = vtkProp3DFollower::SafeDownCast(_prop);
+        vtkSmartPointer<vtkTextActor3D> textActor = vtkTextActor3D::SafeDownCast(follower->GetProp3D());
+        renderer->RemoveViewProp(follower);
+        _prop = textActor;
+        textActor->SetUserMatrix(NULL);
+        textActor->SetUserTransform(NULL);
+        renderer->AddViewProp(_prop);
+    }
+}
+
+void Text3D::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    // FIXME: How to get Mapper?
+}
Index: vtkvis/tags/1.8.4/Text3D.h
===================================================================
--- vtkvis/tags/1.8.4/Text3D.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Text3D.h	(revision 6184)
@@ -0,0 +1,97 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2013  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_TEXT3D_H
+#define VTKVIS_TEXT3D_H
+
+#include <vtkSmartPointer.h>
+#include <vtkTextActor3D.h>
+#include <vtkTextProperty.h>
+#include <vtkProp3DFollower.h>
+#include <vtkRenderer.h>
+
+#include "GraphicsObject.h"
+#include "DataSet.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Text Label with 3D transform
+ *
+ * This class creates a text label that can be positioned in 3D space
+ */
+class Text3D : public GraphicsObject
+{
+public:
+    Text3D();
+    virtual ~Text3D();
+
+    virtual const char *getClassName() const
+    {
+        return "Text3D";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer)
+    {
+        assert(dataSet == NULL);
+        update();
+    }
+
+    virtual void initProp();
+
+    virtual void setOpacity(double opacity);
+
+    virtual void setColor(float color[3]);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setText(const char *string)
+    {
+        vtkTextActor3D *textActor = getTextActor();
+        if (textActor != NULL) {
+            textActor->SetInput(string);
+	}
+    }
+
+    void setFont(const char *fontName);
+
+    void setFontSize(int size);
+
+    void setBold(bool state);
+
+    void setItalic(bool state);
+
+    void setShadow(bool state);
+
+    void setFollowCamera(bool state, vtkRenderer *renderer);
+
+private:
+    virtual void update();
+
+    vtkTextActor3D *getTextActor()
+    {
+        if (vtkTextActor3D::SafeDownCast(_prop) != NULL) {
+            return vtkTextActor3D::SafeDownCast(_prop);
+        } else if (vtkProp3DFollower::SafeDownCast(_prop) != NULL) {
+            return vtkTextActor3D::SafeDownCast(vtkProp3DFollower::SafeDownCast(_prop)->GetProp3D());
+        }
+        return NULL;
+    }
+
+    vtkTextProperty *getTextProperty()
+    {
+        vtkTextActor3D *textActor = getTextActor();
+        if (textActor == NULL)
+            return NULL;
+        return textActor->GetTextProperty();
+    }
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Trace.cpp
===================================================================
--- vtkvis/tags/1.8.4/Trace.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Trace.cpp	(revision 6184)
@@ -0,0 +1,100 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#include <cstdio>
+#include <cstdarg>
+#include <cstring>
+#include <syslog.h>
+
+#include <string>
+#include <sstream>
+
+#include "Trace.h"
+
+using namespace VtkVis;
+
+static std::ostringstream g_UserErrorString;
+
+#define MSG_LEN	2047
+
+/**
+ * \brief Open syslog for writing
+ */
+void
+VtkVis::initLog()
+{
+    openlog("vtkvis", LOG_CONS | LOG_PERROR | LOG_PID,  LOG_USER);
+}
+
+/**
+ * \brief Close syslog
+ */
+void
+VtkVis::closeLog()
+{
+    closelog();
+}
+
+/**
+ * \brief Write a message to syslog
+ */
+void 
+VtkVis::logMessage(int priority, const char *funcname,
+                   const char *path, int lineNum, const char* fmt, ...)
+{
+    char message[MSG_LEN + 1];
+    const char *s;
+    int length;
+    va_list lst;
+
+    va_start(lst, fmt);
+    s = strrchr(path, '/');
+    if (s == NULL) {
+	s = path;
+    } else {
+	s++;
+    }
+    if (priority & LOG_DEBUG) {
+        length = snprintf(message, MSG_LEN, "%s:%d(%s): ", s, lineNum, funcname);
+    } else {
+        length = snprintf(message, MSG_LEN, "%s:%d: ", s, lineNum);
+    }
+    length += vsnprintf(message + length, MSG_LEN - length, fmt, lst);
+    message[MSG_LEN] = '\0';
+
+    syslog(priority, "%s", message);
+}
+
+/**
+ * \brief Write a user message to buffer
+ */
+void 
+VtkVis::logUserMessage(const char* fmt, ...)
+{
+    char message[MSG_LEN + 1];
+    int length = 0;
+    va_list lst;
+
+    va_start(lst, fmt);
+
+    length += vsnprintf(message, MSG_LEN, fmt, lst);
+    message[MSG_LEN] = '\0';
+
+    g_UserErrorString << message << "\n";
+}
+
+std::string
+VtkVis::getUserMessages()
+{
+    return g_UserErrorString.str();
+}
+
+void 
+VtkVis::clearUserMessages()
+{
+    g_UserErrorString.str(std::string());
+}
Index: vtkvis/tags/1.8.4/Trace.h
===================================================================
--- vtkvis/tags/1.8.4/Trace.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Trace.h	(revision 6184)
@@ -0,0 +1,43 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: George A. Howlett <gah@purdue.edu>
+ */
+
+#ifndef VTKVIS_TRACE_H
+#define VTKVIS_TRACE_H
+
+#include <string>
+
+#include <syslog.h>
+
+namespace VtkVis {
+
+extern void logUserMessage(const char* format, ...);
+
+extern std::string getUserMessages();
+
+extern void clearUserMessages();
+
+extern void initLog();
+
+extern void closeLog();
+
+extern void logMessage(int priority, const char *funcname, const char *fileName,
+                       int lineNum, const char* format, ...);
+
+#define ERROR(...)	VtkVis::logMessage(LOG_ERR, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+#ifdef WANT_TRACE
+#define TRACE(...)	VtkVis::logMessage(LOG_DEBUG, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+#else 
+#define TRACE(...) 
+#endif  /*WANT_TRACE*/
+#define WARN(...)	VtkVis::logMessage(LOG_WARNING, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+#define INFO(...)	VtkVis::logMessage(LOG_INFO, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+
+#define USER_ERROR(...) VtkVis::logUserMessage(__VA_ARGS__)
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Types.h
===================================================================
--- vtkvis/tags/1.8.4/Types.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Types.h	(revision 6184)
@@ -0,0 +1,27 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_TYPES_H
+#define VTKVIS_TYPES_H
+
+namespace VtkVis {
+
+    enum Axis {
+        X_AXIS = 0,
+        Y_AXIS,
+        Z_AXIS
+    };
+
+    enum PrincipalPlane {
+        PLANE_XY = 0,
+        PLANE_ZY,
+        PLANE_XZ
+    };
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Volume.cpp
===================================================================
--- vtkvis/tags/1.8.4/Volume.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Volume.cpp	(revision 6184)
@@ -0,0 +1,383 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkImageData.h>
+#include <vtkProbeFilter.h>
+#include <vtkVolumeProperty.h>
+#include <vtkGPUVolumeRayCastMapper.h>
+#ifndef USE_GPU_RAYCAST_MAPPER
+#include <vtkVolumeTextureMapper3D.h>
+#endif
+#include <vtkRectilinearGrid.h>
+#include <vtkStructuredGrid.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkPolyData.h>
+#include <vtkCellType.h>
+#include <vtkUnstructuredGridVolumeMapper.h>
+#include <vtkUnstructuredGridVolumeRayCastMapper.h>
+#include <vtkProjectedTetrahedraMapper.h>
+#include <vtkDataSetTriangleFilter.h>
+#include <vtkGaussianSplatter.h>
+
+#include "Volume.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Volume::Volume() :
+    GraphicsObject(),
+    _useUgridMapper(false),
+    _colorMap(NULL)
+{
+}
+
+Volume::~Volume()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Volume for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Volume with NULL DataSet");
+#endif
+}
+
+/**
+ * \brief Create and initialize a VTK Prop to render the Volume
+ */
+void Volume::initProp()
+{
+    if (_prop == NULL) {
+        _prop = vtkSmartPointer<vtkVolume>::New();
+        getVolume()->GetProperty()->SetInterpolationTypeToLinear();
+    }
+}
+
+/**
+ * \brief Get the voxel dimensions (if the volume is not a
+ * uniform grid, unit spacing is returned)
+ */
+void Volume::getSpacing(double spacing[3])
+{
+    spacing[0] = spacing[1] = spacing[2] = 1.0;
+    if (_dataSet == NULL)
+        return;
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+    if (ds != NULL && vtkImageData::SafeDownCast(ds) != NULL) {
+        vtkImageData::SafeDownCast(ds)->GetSpacing(spacing);
+    } else if (ds != NULL && vtkVolumeMapper::SafeDownCast(_volumeMapper) != NULL) {
+        vtkImageData *imgData = vtkVolumeMapper::SafeDownCast(_volumeMapper)->GetInput();
+        if (imgData != NULL) {
+            imgData->GetSpacing(spacing);
+        }
+    }
+}
+
+/**
+ * \brief Get the average voxel dimension (if the volume is not a
+ * uniform grid, unit spacing is returned)
+ */
+double Volume::getAverageSpacing()
+{
+    double spacing[3];
+    getSpacing(spacing);
+    TRACE("Spacing: %g %g %g", spacing[0], spacing[1], spacing[2]);
+    return (spacing[0] + spacing[1] + spacing[2]) * 0.333;
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void Volume::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    if (_dataSet->is2D()) {
+        USER_ERROR("Volume rendering requires a 3D data set");
+        _dataSet = NULL;
+        return;
+    }
+
+    if (ds->GetPointData() == NULL ||
+        ds->GetPointData()->GetScalars() == NULL) {
+        USER_ERROR("No scalar field was found in the data set");
+        return;
+    }
+
+    if (vtkImageData::SafeDownCast(ds) != NULL) {
+        // Image data required for these mappers
+#ifdef USE_GPU_RAYCAST_MAPPER
+        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
+        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
+#else
+        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
+#endif
+#ifdef USE_GPU_RAYCAST_MAPPER
+        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->SetInputData(ds);
+#else
+        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper)->SetInputData(ds);
+#endif
+        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
+    } else if (_dataSet->isCloud()) {
+        // DataSet is a 3D point cloud
+        vtkSmartPointer<vtkGaussianSplatter> splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+        splatter->SetInputData(ds);
+        int dims[3];
+        dims[0] = dims[1] = dims[2] = 64;
+        if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
+            vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
+        } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
+            vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
+        }
+        TRACE("Generating volume with dims (%d,%d,%d) from %d points",
+              dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
+        splatter->SetSampleDimensions(dims);
+        splatter->Update();
+        TRACE("Done generating volume");
+#ifdef USE_GPU_RAYCAST_MAPPER
+        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
+        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
+#else
+        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
+#endif
+        _volumeMapper->SetInputConnection(splatter->GetOutputPort());
+        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
+    } else if (vtkUnstructuredGrid::SafeDownCast(ds) == NULL || !_useUgridMapper) {
+        // (Slow) Resample using ProbeFilter
+        double bounds[6];
+        ds->GetBounds(bounds);
+        double xLen = bounds[1] - bounds[0];
+        double yLen = bounds[3] - bounds[2];
+        double zLen = bounds[5] - bounds[4];
+
+        int dims[3];
+        dims[0] = dims[1] = dims[2] = 64;
+        if (xLen == 0.0) dims[0] = 1;
+        if (yLen == 0.0) dims[1] = 1;
+        if (zLen == 0.0) dims[2] = 1;
+        if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
+            vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
+        } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
+            vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
+        }
+        TRACE("Generating volume with dims (%d,%d,%d) from %d points",
+              dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
+
+        double xSpacing = (dims[0] == 1 ? 0.0 : xLen/((double)(dims[0]-1)));
+        double ySpacing = (dims[1] == 1 ? 0.0 : yLen/((double)(dims[1]-1)));
+        double zSpacing = (dims[2] == 1 ? 0.0 : zLen/((double)(dims[2]-1)));
+
+        vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
+        imageData->SetDimensions(dims[0], dims[1], dims[2]);
+        imageData->SetOrigin(bounds[0], bounds[2], bounds[4]);
+        imageData->SetSpacing(xSpacing, ySpacing, zSpacing); 
+
+        vtkSmartPointer<vtkProbeFilter> probe = vtkSmartPointer<vtkProbeFilter>::New();
+        probe->SetInputData(imageData);
+        probe->SetSourceData(ds);
+        probe->Update();
+
+        TRACE("Done generating volume");
+
+#ifdef USE_GPU_RAYCAST_MAPPER
+        _volumeMapper = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
+        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->AutoAdjustSampleDistancesOff();
+#else
+        _volumeMapper = vtkSmartPointer<vtkVolumeTextureMapper3D>::New();
+#endif
+        _volumeMapper->SetInputConnection(probe->GetOutputPort());
+        vtkVolumeMapper::SafeDownCast(_volumeMapper)->SetBlendModeToComposite();
+    } else {
+        // Unstructured grid with cells (not a cloud)
+        vtkUnstructuredGrid *ugrid = vtkUnstructuredGrid::SafeDownCast(ds);
+        assert(ugrid != NULL);
+        // DataSet is unstructured grid
+        // Only works if cells are all tetrahedra
+        //_volumeMapper = vtkSmartPointer<vtkProjectedTetrahedraMapper>::New();
+        // Software raycast rendering - requires all tetrahedra
+        _volumeMapper = vtkSmartPointer<vtkUnstructuredGridVolumeRayCastMapper>::New();
+        if (ugrid->GetCellType(0) == VTK_TETRA &&
+            ugrid->IsHomogeneous()) {
+            vtkProjectedTetrahedraMapper::SafeDownCast(_volumeMapper)->SetInputData(ds);
+        } else {
+            // Decompose to tetrahedra
+            vtkSmartPointer<vtkDataSetTriangleFilter> filter = 
+                vtkSmartPointer<vtkDataSetTriangleFilter>::New();
+            filter->SetInputData(ugrid);
+            filter->TetrahedraOnlyOn();
+            TRACE("Decomposing grid to tets");
+            filter->Update();
+            TRACE("Decomposing done");
+            _volumeMapper->SetInputConnection(filter->GetOutputPort());
+        }
+
+        vtkUnstructuredGridVolumeMapper::SafeDownCast(_volumeMapper)->
+            SetBlendModeToComposite();
+    }
+
+    TRACE("Using mapper type: %s", _volumeMapper->GetClassName());
+
+    initProp();
+
+    if (_colorMap == NULL) {
+        setColorMap(ColorMap::getVolumeDefault());
+    }
+
+    setSampleDistance(getAverageSpacing());
+
+    getVolume()->SetMapper(_volumeMapper);
+    _volumeMapper->Update();
+}
+
+void Volume::updateRanges(Renderer *renderer)
+{
+    GraphicsObject::updateRanges(renderer);
+
+    if (getVolume() != NULL) {
+        getVolume()->GetProperty()->SetColor(_colorMap->getColorTransferFunction(_dataRange));
+#ifdef USE_GPU_RAYCAST_MAPPER
+        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange, _opacity));
+#else
+        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange));
+#endif
+    }
+}
+
+void Volume::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Assign a color map (transfer function) to use in rendering the Volume
+ */
+void Volume::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+
+    if (getVolume() != NULL) {
+        getVolume()->GetProperty()->SetColor(_colorMap->getColorTransferFunction(_dataRange));
+#ifdef USE_GPU_RAYCAST_MAPPER
+        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange, _opacity));
+#else
+        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange));
+#endif
+    }
+}
+
+/**
+ * \brief Return the ColorMap assigned to this Volume
+ */
+ColorMap *Volume::getColorMap()
+{
+    return _colorMap;
+}
+
+/**
+ * \brief Set opacity scaling used to render the Volume
+ */
+void Volume::setOpacity(double opacity)
+{
+    _opacity = opacity;
+    // FIXME: There isn't really a good opacity scaling option that works
+    // across the different mappers/algorithms.  This only works with the
+    // 3D texture mapper, not the GPU raycast mapper
+    if (getVolume() != NULL) {
+#ifdef USE_GPU_RAYCAST_MAPPER
+        getVolume()->GetProperty()->SetScalarOpacity(_colorMap->getOpacityTransferFunction(_dataRange, opacity));
+#else
+        if (opacity < 1.0e-6)
+            opacity = 1.0e-6;
+        getVolume()->GetProperty()->SetScalarOpacityUnitDistance(1.0/opacity);
+#endif
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Volume::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_volumeMapper != NULL) {
+        _volumeMapper->SetClippingPlanes(planes);
+    }
+}
+
+/**
+ * \brief Set distance between samples along rays
+ */
+void Volume::setSampleDistance(float d)
+{
+    TRACE("Sample distance: %g", d);
+    if (_volumeMapper != NULL &&
+#ifdef USE_GPU_RAYCAST_MAPPER
+        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper) != NULL) {
+        vtkGPUVolumeRayCastMapper::SafeDownCast(_volumeMapper)->SetSampleDistance(d);
+#else
+        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper) != NULL) {
+        vtkVolumeTextureMapper3D::SafeDownCast(_volumeMapper)->SetSampleDistance(d);
+#endif
+    }
+}
+
+/**
+ * \brief Set Volume renderer blending mode
+ */
+void Volume::setBlendMode(BlendMode mode)
+{
+    if (_volumeMapper == NULL)
+        return;
+
+    vtkVolumeMapper *mapper = vtkVolumeMapper::SafeDownCast(_volumeMapper);
+    if (mapper == NULL) {
+        vtkUnstructuredGridVolumeMapper *ugmapper = vtkUnstructuredGridVolumeMapper::SafeDownCast(_volumeMapper);
+        if (ugmapper == NULL) {
+            TRACE("Mapper does not support BlendMode");
+            return;
+        }
+        switch (mode) {
+        case BLEND_COMPOSITE:
+            ugmapper->SetBlendModeToComposite();
+            break;
+        case BLEND_MAX_INTENSITY:
+            ugmapper->SetBlendModeToMaximumIntensity();
+            break;
+        case BLEND_MIN_INTENSITY:
+        case BLEND_ADDITIVE:
+        default:
+            ERROR("Unknown BlendMode");
+        }
+    } else {
+        switch (mode) {
+        case BLEND_COMPOSITE:
+            mapper->SetBlendModeToComposite();
+            break;
+        case BLEND_MAX_INTENSITY:
+            mapper->SetBlendModeToMaximumIntensity();
+            break;
+        case BLEND_MIN_INTENSITY:
+            mapper->SetBlendModeToMinimumIntensity();
+            break;
+        case BLEND_ADDITIVE:
+            mapper->SetBlendModeToAdditive();
+            break;
+        default:
+            ERROR("Unknown BlendMode");
+        }
+    }
+}
Index: vtkvis/tags/1.8.4/Volume.h
===================================================================
--- vtkvis/tags/1.8.4/Volume.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Volume.h	(revision 6184)
@@ -0,0 +1,77 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_VOLUME_H
+#define VTKVIS_VOLUME_H
+
+#include <vtkSmartPointer.h>
+#include <vtkProp.h>
+#include <vtkVolume.h>
+#include <vtkAbstractVolumeMapper.h>
+#include <vtkPlaneCollection.h>
+
+#include "GraphicsObject.h"
+#include "ColorMap.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Volume Rendering
+ *
+ * Currently the DataSet must be image data (3D uniform grid),
+ * or an UnstructuredGrid
+ */
+class Volume : public GraphicsObject {
+public:
+    enum BlendMode {
+        BLEND_COMPOSITE = 0,
+        BLEND_MAX_INTENSITY,
+        BLEND_MIN_INTENSITY,
+        BLEND_ADDITIVE
+    };
+
+    Volume();
+    virtual ~Volume();
+
+    virtual const char *getClassName() const
+    {
+        return "Volume";
+    }
+
+    void getSpacing(double spacing[3]);
+
+    double getAverageSpacing();
+
+    virtual void setOpacity(double opacity);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setColorMap(ColorMap *cmap);
+
+    ColorMap *getColorMap();
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+    void setSampleDistance(float d);
+
+    void setBlendMode(BlendMode mode);
+
+private:
+    virtual void initProp();
+    virtual void update();
+
+    bool _useUgridMapper;
+
+    ColorMap *_colorMap;
+    vtkSmartPointer<vtkAbstractVolumeMapper> _volumeMapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/Warp.cpp
===================================================================
--- vtkvis/tags/1.8.4/Warp.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/Warp.cpp	(revision 6184)
@@ -0,0 +1,547 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#include <cassert>
+#include <cfloat>
+#include <cstring>
+
+#include <vtkDataSet.h>
+#include <vtkPointData.h>
+#include <vtkCellData.h>
+#include <vtkCellDataToPointData.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkUnstructuredGrid.h>
+#include <vtkProperty.h>
+#include <vtkImageData.h>
+#include <vtkLookupTable.h>
+#include <vtkTransform.h>
+#include <vtkDelaunay2D.h>
+#include <vtkDelaunay3D.h>
+#include <vtkGaussianSplatter.h>
+#include <vtkExtractVOI.h>
+#include <vtkDataSetSurfaceFilter.h>
+#include <vtkVertexGlyphFilter.h>
+
+#include "Warp.h"
+#include "Renderer.h"
+#include "Trace.h"
+
+using namespace VtkVis;
+
+Warp::Warp() :
+    GraphicsObject(),
+    _warpScale(1.0),
+    _cloudStyle(CLOUD_MESH),
+    _colorMap(NULL),
+    _colorMode(COLOR_BY_SCALAR),
+    _colorFieldType(DataSet::POINT_DATA),
+    _renderer(NULL)
+{
+    _colorFieldRange[0] = DBL_MAX;
+    _colorFieldRange[1] = -DBL_MAX;
+}
+
+Warp::~Warp()
+{
+#ifdef WANT_TRACE
+    if (_dataSet != NULL)
+        TRACE("Deleting Warp for %s", _dataSet->getName().c_str());
+    else
+        TRACE("Deleting Warp with NULL DataSet");
+#endif
+}
+
+void Warp::setDataSet(DataSet *dataSet,
+                      Renderer *renderer)
+{
+    if (_dataSet != dataSet) {
+        _dataSet = dataSet;
+
+        _renderer = renderer;
+
+        if (renderer->getUseCumulativeRange()) {
+            renderer->getCumulativeDataRange(_dataRange,
+                                             _dataSet->getActiveScalarsName(),
+                                             1);
+            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                             _dataSet->getActiveVectorsName(),
+                                             3);
+            for (int i = 0; i < 3; i++) {
+                renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                                 _dataSet->getActiveVectorsName(),
+                                                 3, i);
+            }
+        } else {
+            _dataSet->getScalarRange(_dataRange);
+            _dataSet->getVectorRange(_vectorMagnitudeRange);
+            for (int i = 0; i < 3; i++) {
+                _dataSet->getVectorRange(_vectorComponentRange[i], i);
+            }
+        }
+
+        update();
+    }
+}
+
+/**
+ * \brief Internal method to set up pipeline after a state change
+ */
+void Warp::update()
+{
+    if (_dataSet == NULL)
+        return;
+
+    vtkDataSet *ds = _dataSet->getVtkDataSet();
+
+    if (_warp == NULL) {
+        _warp = vtkSmartPointer<vtkWarpVector>::New();
+    }
+
+    // Mapper, actor to render color-mapped data set
+    if (_mapper == NULL) {
+        _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        // Map scalars through lookup table regardless of type
+        _mapper->SetColorModeToMapScalars();
+    }
+
+    vtkAlgorithmOutput *dsOutput = NULL;
+    vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
+    if (ds->GetPointData() == NULL ||
+        ds->GetPointData()->GetVectors() == NULL) {
+        TRACE("No vector point data in dataset %s", _dataSet->getName().c_str());
+        if (ds->GetCellData() != NULL &&
+            ds->GetCellData()->GetVectors() != NULL) {
+            cellToPtData = 
+                vtkSmartPointer<vtkCellDataToPointData>::New();
+            cellToPtData->SetInputData(ds);
+            cellToPtData->PassCellDataOn();
+            dsOutput = cellToPtData->GetOutputPort();
+        } else {
+            USER_ERROR("No vector field was found in the data set");
+            return;
+        }
+    } else {
+        dsOutput = _dataSet->getProducerPort();
+    }
+
+    if (_dataSet->isCloud()) {
+        // DataSet is a point cloud
+        if (_cloudStyle == CLOUD_POINTS) {
+            vtkSmartPointer<vtkVertexGlyphFilter> vgf = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+            vgf->SetInputConnection(dsOutput);
+            vtkAlgorithmOutput *warpOutput = initWarp(vgf->GetOutputPort());
+            _mapper->SetInputConnection(warpOutput);
+        } else {
+            PrincipalPlane plane;
+            double offset;
+            if (_dataSet->is2D(&plane, &offset)) {
+                //_cloudStyle == CLOUD_MESH
+                vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                if (plane == PLANE_ZY) {
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->RotateWXYZ(90, 0, 1, 0);
+                    if (offset != 0.0) {
+                        trans->Translate(-offset, 0, 0);
+                    }
+                    mesher->SetTransform(trans);
+                } else if (plane == PLANE_XZ) {
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->RotateWXYZ(-90, 1, 0, 0);
+                    if (offset != 0.0) {
+                        trans->Translate(0, -offset, 0);
+                    }
+                    mesher->SetTransform(trans);
+                } else if (offset != 0.0) {
+                    // XY with Z offset
+                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
+                    trans->Translate(0, 0, -offset);
+                    mesher->SetTransform(trans);
+                }
+                mesher->SetInputConnection(dsOutput);
+                vtkAlgorithmOutput *warpOutput = initWarp(mesher->GetOutputPort());
+                _mapper->SetInputConnection(warpOutput);
+            } else {
+                // Data Set is a 3D point cloud and _cloudStyle == CLOUD_MESH
+                vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
+                mesher->SetInputConnection(dsOutput);
+                vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+                gf->SetInputConnection(mesher->GetOutputPort());
+                vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
+                _mapper->SetInputConnection(warpOutput);
+            }
+        }
+    } else if (vtkPolyData::SafeDownCast(ds) != NULL) {
+        // DataSet is a vtkPolyData with lines and/or polygons
+        vtkAlgorithmOutput *warpOutput = initWarp(dsOutput);
+        _mapper->SetInputConnection(warpOutput);
+    } else {
+        TRACE("Generating surface for data set");
+        // DataSet is NOT a vtkPolyData
+        vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+        gf->SetInputConnection(dsOutput);
+        vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
+        _mapper->SetInputConnection(warpOutput);
+    }
+
+    setInterpolateBeforeMapping(true);
+
+    if (_lut == NULL) {
+        setColorMap(ColorMap::getDefault());
+    }
+
+    if (_dataSet->getActiveScalarsName() != NULL) {
+        TRACE("Setting color mode to scalar");
+        setColorMode(COLOR_BY_SCALAR);
+    } else {
+        TRACE("Setting color mode to vector magnitude");
+        setColorMode(COLOR_BY_VECTOR_MAGNITUDE);
+    }
+
+    initProp();
+    getActor()->SetMapper(_mapper);
+    _mapper->Update();
+}
+
+void Warp::setCloudStyle(CloudStyle style)
+{
+    if (style != _cloudStyle) {
+        _cloudStyle = style;
+        if (_dataSet != NULL) {
+            update();
+        }
+    }
+}
+
+void Warp::setInterpolateBeforeMapping(bool state)
+{
+    if (_mapper != NULL) {
+        _mapper->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
+    }
+}
+
+vtkAlgorithmOutput *Warp::initWarp(vtkAlgorithmOutput *input)
+{
+    TRACE("Warp scale: %g", _warpScale);
+    if (_warpScale == 0.0) {
+        _warp = NULL;
+        return input;
+    } else if (input == NULL) {
+        ERROR("NULL input");
+        return input;
+    } else {
+        if (_warp == NULL)
+            _warp = vtkSmartPointer<vtkWarpVector>::New();
+        _warp->SetScaleFactor(_warpScale);
+        _warp->SetInputConnection(input);
+        return _warp->GetOutputPort();
+    }
+}
+
+/**
+ * \brief Controls relative scaling of deformation
+ */
+void Warp::setWarpScale(double scale)
+{
+    if (_warpScale == scale)
+        return;
+
+    _warpScale = scale;
+    if (_warp == NULL) {
+        vtkAlgorithmOutput *warpOutput = initWarp(_mapper->GetInputConnection(0, 0));
+        _mapper->SetInputConnection(warpOutput);
+    } else if (scale == 0.0) {
+        vtkAlgorithmOutput *warpInput = _warp->GetInputConnection(0, 0);
+        _mapper->SetInputConnection(warpInput);
+        _warp = NULL;
+    } else {
+        _warp->SetScaleFactor(_warpScale);
+    }
+
+    if (_mapper != NULL)
+        _mapper->Update();
+}
+
+void Warp::updateRanges(Renderer *renderer)
+{
+    if (_dataSet == NULL) {
+        ERROR("called before setDataSet");
+        return;
+    }
+
+    if (renderer->getUseCumulativeRange()) {
+        renderer->getCumulativeDataRange(_dataRange,
+                                         _dataSet->getActiveScalarsName(),
+                                         1);
+        renderer->getCumulativeDataRange(_vectorMagnitudeRange,
+                                         _dataSet->getActiveVectorsName(),
+                                         3);
+        for (int i = 0; i < 3; i++) {
+            renderer->getCumulativeDataRange(_vectorComponentRange[i],
+                                             _dataSet->getActiveVectorsName(),
+                                             3, i);
+        }
+    } else {
+        _dataSet->getScalarRange(_dataRange);
+        _dataSet->getVectorRange(_vectorMagnitudeRange);
+        for (int i = 0; i < 3; i++) {
+            _dataSet->getVectorRange(_vectorComponentRange[i], i);
+        }
+    }
+
+    // Need to update color map ranges
+    double *rangePtr = _colorFieldRange;
+    if (_colorFieldRange[0] > _colorFieldRange[1]) {
+        rangePtr = NULL;
+    }
+    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
+}
+
+void Warp::setColorMode(ColorMode mode)
+{
+    _colorMode = mode;
+    if (_dataSet == NULL)
+        return;
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        setColorMode(mode,
+                     _dataSet->getActiveScalarsType(),
+                     _dataSet->getActiveScalarsName());
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_X:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Y:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_BY_VECTOR_Z:
+        setColorMode(mode,
+                     _dataSet->getActiveVectorsType(),
+                     _dataSet->getActiveVectorsName());
+        break;
+    case COLOR_CONSTANT:
+    default:
+        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
+        break;
+    }
+}
+
+void Warp::setColorMode(ColorMode mode,
+                        const char *name, double range[2])
+{
+    if (_dataSet == NULL)
+        return;
+    DataSet::DataAttributeType type = DataSet::POINT_DATA;
+    int numComponents = 1;
+    if (name != NULL && strlen(name) > 0 &&
+        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
+        ERROR("Field not found: %s", name);
+        return;
+    }
+    setColorMode(mode, type, name, range);
+}
+
+void Warp::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                        const char *name, double range[2])
+{
+    _colorMode = mode;
+    _colorFieldType = type;
+    if (name == NULL)
+        _colorFieldName.clear();
+    else
+        _colorFieldName = name;
+    if (range == NULL) {
+        _colorFieldRange[0] = DBL_MAX;
+        _colorFieldRange[1] = -DBL_MAX;
+    } else {
+        memcpy(_colorFieldRange, range, sizeof(double)*2);
+    }
+
+    if (_dataSet == NULL || _mapper == NULL)
+        return;
+
+    switch (type) {
+    case DataSet::POINT_DATA:
+        _mapper->SetScalarModeToUsePointFieldData();
+        break;
+    case DataSet::CELL_DATA:
+        _mapper->SetScalarModeToUseCellFieldData();
+        break;
+    default:
+        ERROR("Unsupported DataAttributeType: %d", type);
+        return;
+    }
+
+    if (name != NULL && strlen(name) > 0) {
+        _mapper->SelectColorArray(name);
+    } else {
+        _mapper->SetScalarModeToDefault();
+    }
+
+    if (_lut != NULL) {
+        if (range != NULL) {
+            _lut->SetRange(range);
+        } else if (name != NULL && strlen(name) > 0) {
+            double r[2];
+            int comp = -1;
+            if (mode == COLOR_BY_VECTOR_X)
+                comp = 0;
+            else if (mode == COLOR_BY_VECTOR_Y)
+                comp = 1;
+            else if (mode == COLOR_BY_VECTOR_Z)
+                comp = 2;
+
+            if (_renderer->getUseCumulativeRange()) {
+                int numComponents;
+                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
+                    ERROR("Field not found: %s, type: %d", name, type);
+                    return;
+                } else if (numComponents < comp+1) {
+                    ERROR("Request for component %d in field with %d components",
+                          comp, numComponents);
+                    return;
+                }
+                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
+            } else {
+                _dataSet->getDataRange(r, name, type, comp);
+            }
+            _lut->SetRange(r);
+        }
+    }
+
+    switch (mode) {
+    case COLOR_BY_SCALAR:
+        _mapper->ScalarVisibilityOn();
+        break;
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToMagnitude();
+        }
+        break;
+    case COLOR_BY_VECTOR_X:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(0);
+        }
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(1);
+        }
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _mapper->ScalarVisibilityOn();
+        if (_lut != NULL) {
+            _lut->SetVectorModeToComponent();
+            _lut->SetVectorComponent(2);
+        }
+        break;
+    case COLOR_CONSTANT:
+    default:
+        _mapper->ScalarVisibilityOff();
+        break;
+    }
+}
+
+/**
+ * \brief Called when the color map has been edited
+ */
+void Warp::updateColorMap()
+{
+    setColorMap(_colorMap);
+}
+
+/**
+ * \brief Associate a colormap lookup table with the DataSet
+ */
+void Warp::setColorMap(ColorMap *cmap)
+{
+    if (cmap == NULL)
+        return;
+
+    _colorMap = cmap;
+ 
+    if (_lut == NULL) {
+        _lut = vtkSmartPointer<vtkLookupTable>::New();
+        if (_mapper != NULL) {
+            _mapper->UseLookupTableScalarRangeOn();
+            _mapper->SetLookupTable(_lut);
+        }
+        _lut->DeepCopy(cmap->getLookupTable());
+        switch (_colorMode) {
+        case COLOR_CONSTANT:
+        case COLOR_BY_SCALAR:
+            _lut->SetRange(_dataRange);
+            break;
+        case COLOR_BY_VECTOR_MAGNITUDE:
+            _lut->SetRange(_vectorMagnitudeRange);
+            break;
+        case COLOR_BY_VECTOR_X:
+            _lut->SetRange(_vectorComponentRange[0]);
+            break;
+        case COLOR_BY_VECTOR_Y:
+            _lut->SetRange(_vectorComponentRange[1]);
+            break;
+        case COLOR_BY_VECTOR_Z:
+            _lut->SetRange(_vectorComponentRange[2]);
+            break;
+        default:
+            break;
+        }
+    } else {
+        double range[2];
+        _lut->GetTableRange(range);
+        _lut->DeepCopy(cmap->getLookupTable());
+        _lut->SetRange(range);
+        _lut->Modified();
+    }
+
+    switch (_colorMode) {
+    case COLOR_BY_VECTOR_MAGNITUDE:
+        _lut->SetVectorModeToMagnitude();
+        break;
+    case COLOR_BY_VECTOR_X:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(0);
+        break;
+    case COLOR_BY_VECTOR_Y:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(1);
+        break;
+    case COLOR_BY_VECTOR_Z:
+        _lut->SetVectorModeToComponent();
+        _lut->SetVectorComponent(2);
+        break;
+    default:
+         break;
+    }
+}
+
+/**
+ * \brief Set a group of world coordinate planes to clip rendering
+ *
+ * Passing NULL for planes will remove all cliping planes
+ */
+void Warp::setClippingPlanes(vtkPlaneCollection *planes)
+{
+    if (_mapper != NULL) {
+        _mapper->SetClippingPlanes(planes);
+    }
+}
Index: vtkvis/tags/1.8.4/Warp.h
===================================================================
--- vtkvis/tags/1.8.4/Warp.h	(revision 6184)
+++ vtkvis/tags/1.8.4/Warp.h	(revision 6184)
@@ -0,0 +1,116 @@
+/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2004-2012  HUBzero Foundation, LLC
+ *
+ * Author: Leif Delgass <ldelgass@purdue.edu>
+ */
+
+#ifndef VTKVIS_WARP_H
+#define VTKVIS_WARP_H
+
+#include <vtkSmartPointer.h>
+#include <vtkAlgorithmOutput.h>
+#include <vtkLookupTable.h>
+#include <vtkDataSet.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkActor.h>
+#include <vtkPlaneCollection.h>
+#include <vtkWarpVector.h>
+
+#include "ColorMap.h"
+#include "GraphicsObject.h"
+
+namespace VtkVis {
+
+/**
+ * \brief Warp a mesh based on point data vectors
+ *
+ * This class can be used to visualize mechanical deformation or 
+ * to create flow profiles/surfaces
+ */
+class Warp : public GraphicsObject {
+public:
+    enum CloudStyle {
+        CLOUD_MESH,
+        CLOUD_POINTS
+    };
+    enum ColorMode {
+        COLOR_BY_SCALAR,
+        COLOR_BY_VECTOR_MAGNITUDE,
+        COLOR_BY_VECTOR_X,
+        COLOR_BY_VECTOR_Y,
+        COLOR_BY_VECTOR_Z,
+        COLOR_CONSTANT
+    };
+
+    Warp();
+    virtual ~Warp();
+
+    virtual const char *getClassName() const
+    {
+        return "Warp";
+    }
+
+    virtual void setDataSet(DataSet *dataSet,
+                            Renderer *renderer);
+
+    virtual void setClippingPlanes(vtkPlaneCollection *planes);
+
+    void setWarpScale(double scale);
+
+    double getWarpScale()
+    {
+        return _warpScale;
+    }
+    
+    void setCloudStyle(CloudStyle style);
+
+    void setInterpolateBeforeMapping(bool state);
+
+    void setColorMode(ColorMode mode, DataSet::DataAttributeType type,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode,
+                      const char *name, double range[2] = NULL);
+
+    void setColorMode(ColorMode mode);
+
+    void setColorMap(ColorMap *colorMap);
+
+    /**
+     * \brief Return the ColorMap in use
+     */
+    ColorMap *getColorMap()
+    {
+        return _colorMap;
+    }
+
+    void updateColorMap();
+
+    virtual void updateRanges(Renderer *renderer);
+
+private:
+    virtual void update();
+
+    vtkAlgorithmOutput *initWarp(vtkAlgorithmOutput *input);
+
+    double _warpScale;
+
+    CloudStyle _cloudStyle;
+    ColorMap *_colorMap;
+    ColorMode _colorMode;
+    std::string _colorFieldName;
+    DataSet::DataAttributeType _colorFieldType;
+    double _colorFieldRange[2];
+    double _vectorMagnitudeRange[2];
+    double _vectorComponentRange[3][2];
+    Renderer *_renderer;
+
+    vtkSmartPointer<vtkLookupTable> _lut;
+    vtkSmartPointer<vtkWarpVector> _warp;
+    vtkSmartPointer<vtkPolyDataMapper> _mapper;
+};
+
+}
+
+#endif
Index: vtkvis/tags/1.8.4/cf/install-sh
===================================================================
--- vtkvis/tags/1.8.4/cf/install-sh	(revision 6184)
+++ vtkvis/tags/1.8.4/cf/install-sh	(revision 6184)
@@ -0,0 +1,269 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+#
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+		chmodcmd=""
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS=' 	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
Index: vtkvis/tags/1.8.4/cf/mkinstalldirs
===================================================================
--- vtkvis/tags/1.8.4/cf/mkinstalldirs	(revision 6184)
+++ vtkvis/tags/1.8.4/cf/mkinstalldirs	(revision 6184)
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id: mkinstalldirs,v 1.13 1999/01/05 03:18:55 bje Exp $
+
+errstatus=0
+
+for file
+do
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d
+   do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp"
+
+        mkdir "$pathcomp" || lasterr=$?
+
+        if test ! -d "$pathcomp"; then
+  	  errstatus=$lasterr
+        fi
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
Index: vtkvis/tags/1.8.4/configure
===================================================================
--- vtkvis/tags/1.8.4/configure	(revision 6184)
+++ vtkvis/tags/1.8.4/configure	(revision 6184)
@@ -0,0 +1,5666 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for vtkvis 1.8.
+#
+# Report bugs to <rappture@nanohub.org>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir/$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: rappture@nanohub.org about your system, including any
+$0: error possibly output before this message. Then install
+$0: a modern shell, or manually run the script under such a
+$0: shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='vtkvis'
+PACKAGE_TARNAME='vtkvis'
+PACKAGE_VERSION='1.8'
+PACKAGE_STRING='vtkvis 1.8'
+PACKAGE_BUGREPORT='rappture@nanohub.org'
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+VTK_LIB_DIR
+VTK_INC_DIR
+VTK_VERSION
+TCL_VERSION
+TCL_LIB_SPEC
+TCL_INC_SPEC
+STATSDIR
+EGREP
+GREP
+CXXCPP
+ac_ct_CXX
+CXXFLAGS
+CXX
+CPP
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+MKDIR_P
+LN_S
+RANLIB
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_tcllib
+with_vtk_includes
+with_vtk_libs
+with_vtk
+with_statsdir
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+CXX
+CXXFLAGS
+CCC
+CXXCPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures vtkvis 1.8 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/vtkvis]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of vtkvis 1.8:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-tcllib=DIR       location of Tcl binary library libtclstubs.a
+                          [default=/usr/lib]
+  --with-vtk-includes=DIR location of versioned VTK include dir
+  --with-vtk-libs=DIR     VTK libraries are in DIR
+  --with-vtk=VERSION      Version of VTK required [default=6.2]
+  --with-statsdir=DIR     Write statistics in DIR
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  CXXCPP      C++ preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <rappture@nanohub.org>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+vtkvis configure 1.8
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+
+# ac_fn_cxx_try_cpp LINENO
+# ------------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_cpp
+
+# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES
+# ---------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_cxx_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ----------------------------------- ##
+## Report this to rappture@nanohub.org ##
+## ----------------------------------- ##"
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_header_mongrel
+
+# ac_fn_cxx_try_run LINENO
+# ------------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_cxx_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_run
+
+# ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES
+# ---------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_cxx_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_header_compile
+
+# ac_fn_cxx_try_link LINENO
+# -------------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_link
+
+# ac_fn_cxx_check_func LINENO FUNC VAR
+# ------------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_cxx_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_cxx_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by vtkvis $as_me 1.8, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	$as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_aux_dir=
+for ac_dir in cf "$srcdir"/cf; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in cf \"$srcdir\"/cf" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+
+
+# Check whether --with-tcllib was given.
+if test "${with_tcllib+set}" = set; then :
+  withval=$with_tcllib; with_tcllib=$withval
+else
+  with_tcllib=""
+fi
+
+
+
+# Check whether --with-vtk-includes was given.
+if test "${with_vtk_includes+set}" = set; then :
+  withval=$with_vtk_includes; with_vtk_includes=$withval
+else
+  with_vtk_includes=""
+fi
+
+
+
+# Check whether --with-vtk-libs was given.
+if test "${with_vtk_libs+set}" = set; then :
+  withval=$with_vtk_libs; with_vtk_libs=$withval
+else
+  with_vtk_libs=""
+fi
+
+
+
+# Check whether --with-vtk was given.
+if test "${with_vtk+set}" = set; then :
+  withval=$with_vtk; with_vtk=$withval
+else
+  with_vtk=""
+fi
+
+
+
+# Check whether --with-statsdir was given.
+if test "${with_statsdir+set}" = set; then :
+  withval=$with_statsdir; STATSDIR=$withval
+else
+  STATSDIR="/var/tmp/visservers"
+fi
+
+
+#------------------------------------------------------------------------
+# Handle the --prefix=... option
+#------------------------------------------------------------------------
+
+if test "${prefix}" = "NONE"; then
+    prefix=/usr/local
+fi
+if test "${exec_prefix}" = "NONE"; then
+    exec_prefix=$prefix
+fi
+
+if test "x${incdir}" == "x"; then
+  incdir=${exec_prefix}/include
+fi
+libdir=${exec_prefix}/lib
+
+if test "${libdir}" != "${prefix}/lib"; then
+    LIB_SEARCH_DIRS="-L ${prefix}/lib -L ${libdir}"
+else
+    LIB_SEARCH_DIRS="-L ${libdir}"
+fi
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    rm -rf conftest.one conftest.two conftest.dir
+	    echo one > conftest.one
+	    echo two > conftest.two
+	    mkdir conftest.dir
+	    if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+	      test -s conftest.one && test -s conftest.two &&
+	      test -s conftest.dir/conftest.one &&
+	      test -s conftest.dir/conftest.two
+	    then
+	      ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	      break 3
+	    fi
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if ${ac_cv_path_mkdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in mkdir gmkdir; do
+	 for ac_exec_ext in '' $ac_executable_extensions; do
+	   as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
+	   case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+	     'mkdir (GNU coreutils) '* | \
+	     'mkdir (coreutils) '* | \
+	     'mkdir (fileutils) '4.1*)
+	       ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+	       break 3;;
+	   esac
+	 done
+       done
+  done
+IFS=$as_save_IFS
+
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test "${ac_cv_path_mkdir+set}" = set; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+	 CXXFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5
+$as_echo_n "checking how to run the C++ preprocessor... " >&6; }
+if test -z "$CXXCPP"; then
+  if ${ac_cv_prog_CXXCPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CXXCPP needs to be expanded
+    for CXXCPP in "$CXX -E" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CXXCPP=$CXXCPP
+
+fi
+  CXXCPP=$ac_cv_prog_CXXCPP
+else
+  ac_cv_prog_CXXCPP=$CXXCPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5
+$as_echo "$CXXCPP" >&6; }
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+	 CXXFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+#
+# Need to check for the following dependencies:
+#       Tcl headers and library
+#       GL headers and library
+#       pthread library
+#       vtk libraries
+#
+
+TCL_VERSION="8.4"
+for dir in \
+ ${exec_prefix} \
+ ${exec_prefix}/lib ; do
+  tclconfig="${dir}/tclConfig.sh"
+  if test -f "$tclconfig" ; then
+    . $tclconfig
+    break
+  fi
+done
+TCL_INC_SPEC="$TCL_INCLUDE_SPEC"
+
+if test "x$with_tcllib" != "x" ; then
+   tclconfig="${with_tcllib}/tclConfig.sh"
+   if test -f "$tclconfig" ; then
+    . $tclconfig
+   fi
+   TCL_LIB_SPEC="-L${with_tcllib} -ltcl${TCL_VERSION}"
+fi
+if test "x$with_tclinclude" != "x" ; then
+   TCL_INC_SPEC="-I${with_tclinclude}"
+fi
+
+# There can be multiple versions of VTK installed side-by-side
+VTK_VERSION=6.2
+VTK_LIB_DIR=${libdir}
+VTK_INC_DIR=${incdir}
+
+if test "x$with_vtk_includes" != "x"; then
+   VTK_INC_DIR=$with_vtk_includes
+fi
+
+if test "x$with_vtk_libs" != "x"; then
+   VTK_LIB_DIR=$with_vtk_libs
+fi
+
+if test "x$with_vtk" != "x" ; then
+   VTK_VERSION=$with_vtk
+fi
+
+if ! test -d "$VTK_INC_DIR/vtk-${VTK_VERSION}" ; then
+   as_fn_error $? "Couldn't find VTK includes" "$LINENO" 5
+else
+   VTK_INC_SPEC="-I${VTK_INC_DIR}/vtk-${VTK_VERSION}"
+fi
+
+save_CPPFLAGS=$CPPFLAGS
+CPPFLAGS=$VTK_INC_SPEC
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in vtkSmartPointer.h
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "vtkSmartPointer.h" "ac_cv_header_vtkSmartPointer_h" "$ac_includes_default"
+if test "x$ac_cv_header_vtkSmartPointer_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_VTKSMARTPOINTER_H 1
+_ACEOF
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find vtkSmartPointer.h" >&5
+$as_echo "$as_me: WARNING: cannot find vtkSmartPointer.h" >&2;}
+fi
+
+done
+
+CPPFLAGS=$save_CPPFLAGS
+
+for ac_header in stdio.h unistd.h stdlib.h string.h sys/types.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in sys/socket.h sys/time.h netinet/in.h arpa/inet.h netdb.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_func in accept4
+do :
+  ac_fn_cxx_check_func "$LINENO" "accept4" "ac_cv_func_accept4"
+if test "x$ac_cv_func_accept4" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_ACCEPT4 1
+_ACEOF
+
+fi
+done
+
+
+
+
+
+
+
+
+
+
+ac_config_files="$ac_config_files Doxyfile Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+:mline
+/\\$/{
+ N
+ s,\\\n,,
+ b mline
+}
+t clear
+:clear
+s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*([^)]*)\)[	 ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[	 `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+	g
+	s/^\n//
+	s/\n/ /g
+	p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by vtkvis $as_me 1.8, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <rappture@nanohub.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+vtkvis config.status 1.8
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h |  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "Doxyfile") CONFIG_FILES="$CONFIG_FILES Doxyfile" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = ""
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+
+eval set X "  :F $CONFIG_FILES      "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
Index: vtkvis/tags/1.8.4/configure.in
===================================================================
--- vtkvis/tags/1.8.4/configure.in	(revision 6184)
+++ vtkvis/tags/1.8.4/configure.in	(revision 6184)
@@ -0,0 +1,148 @@
+AC_PREREQ([2.60])
+AC_INIT([vtkvis], [1.8], [rappture@nanohub.org])
+AC_CONFIG_AUX_DIR(cf)
+
+AC_ARG_WITH(
+    [tcllib],
+    [AS_HELP_STRING([--with-tcllib[=DIR]],
+        [location of Tcl binary library libtclstubs.a @<:@default=/usr/lib@:>@])],
+    [with_tcllib=$withval],
+    [with_tcllib=""])
+
+AC_ARG_WITH(
+    [vtk-includes],
+    [AS_HELP_STRING([--with-vtk-includes=DIR],
+        [location of versioned VTK include dir])],
+    [with_vtk_includes=$withval],
+    [with_vtk_includes=""])
+
+AC_ARG_WITH(
+    [vtk-libs],
+    [AS_HELP_STRING([--with-vtk-libs=DIR],
+        [VTK libraries are in DIR])],
+    [with_vtk_libs=$withval],
+    [with_vtk_libs=""])
+
+AC_ARG_WITH(
+    [vtk],
+    [AS_HELP_STRING([--with-vtk=VERSION],
+        [Version of VTK required @<:@default=6.2@:>@])],
+    [with_vtk=$withval],
+    [with_vtk=""])
+
+AC_ARG_WITH(
+    [statsdir],
+    [AS_HELP_STRING([--with-statsdir=DIR],
+        [Write statistics in DIR])],
+    [STATSDIR=$withval],
+    [STATSDIR="/var/tmp/visservers"])
+
+#------------------------------------------------------------------------
+# Handle the --prefix=... option
+#------------------------------------------------------------------------
+
+if test "${prefix}" = "NONE"; then
+    prefix=/usr/local
+fi
+if test "${exec_prefix}" = "NONE"; then
+    exec_prefix=$prefix
+fi
+
+if test "x${incdir}" == "x"; then
+  incdir=${exec_prefix}/include
+fi
+libdir=${exec_prefix}/lib
+
+if test "${libdir}" != "${prefix}/lib"; then
+    LIB_SEARCH_DIRS="-L ${prefix}/lib -L ${libdir}"
+else
+    LIB_SEARCH_DIRS="-L ${libdir}"
+fi
+
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_PROG_LN_S
+AC_PROG_MKDIR_P
+
+dnl find and test the C compiler
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_CXXCPP
+AC_PROG_CXX
+AC_LANG_CPLUSPLUS
+
+#
+# Need to check for the following dependencies:
+#       Tcl headers and library
+#       GL headers and library
+#       pthread library
+#       vtk libraries
+#
+
+TCL_VERSION="8.4"
+for dir in \
+ ${exec_prefix} \
+ ${exec_prefix}/lib ; do
+  tclconfig="${dir}/tclConfig.sh"
+  if test -f "$tclconfig" ; then
+    . $tclconfig
+    break
+  fi
+done
+TCL_INC_SPEC="$TCL_INCLUDE_SPEC"
+
+if test "x$with_tcllib" != "x" ; then
+   tclconfig="${with_tcllib}/tclConfig.sh"
+   if test -f "$tclconfig" ; then
+    . $tclconfig
+   fi
+   TCL_LIB_SPEC="-L${with_tcllib} -ltcl${TCL_VERSION}"
+fi
+if test "x$with_tclinclude" != "x" ; then
+   TCL_INC_SPEC="-I${with_tclinclude}"
+fi
+
+# There can be multiple versions of VTK installed side-by-side
+VTK_VERSION=6.2
+VTK_LIB_DIR=${libdir}
+VTK_INC_DIR=${incdir}
+
+if test "x$with_vtk_includes" != "x"; then
+   VTK_INC_DIR=$with_vtk_includes
+fi
+
+if test "x$with_vtk_libs" != "x"; then
+   VTK_LIB_DIR=$with_vtk_libs
+fi
+
+if test "x$with_vtk" != "x" ; then
+   VTK_VERSION=$with_vtk
+fi
+
+if ! test -d "$VTK_INC_DIR/vtk-${VTK_VERSION}" ; then
+   AC_ERROR([Couldn't find VTK includes])
+else
+   VTK_INC_SPEC="-I${VTK_INC_DIR}/vtk-${VTK_VERSION}"
+fi
+
+save_CPPFLAGS=$CPPFLAGS
+CPPFLAGS=$VTK_INC_SPEC
+AC_CHECK_HEADERS([vtkSmartPointer.h], [], [AC_MSG_WARN([cannot find vtkSmartPointer.h])])
+CPPFLAGS=$save_CPPFLAGS
+
+AC_CHECK_HEADERS([stdio.h unistd.h stdlib.h string.h sys/types.h])
+AC_CHECK_HEADERS([sys/socket.h sys/time.h netinet/in.h arpa/inet.h netdb.h])
+
+AC_CHECK_FUNCS([accept4])
+
+AC_SUBST(STATSDIR)
+AC_SUBST(TCL_INC_SPEC)
+AC_SUBST(TCL_LIB_SPEC)
+AC_SUBST(TCL_VERSION)
+AC_SUBST(VTK_VERSION)
+AC_SUBST(VTK_INC_DIR)
+AC_SUBST(VTK_LIB_DIR)
+
+AC_OUTPUT([
+   Doxyfile \
+   Makefile])
Index: vtkvis/tags/1.8.4/md5.c
===================================================================
--- vtkvis/tags/1.8.4/md5.c	(revision 6184)
+++ vtkvis/tags/1.8.4/md5.c	(revision 6184)
@@ -0,0 +1,381 @@
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+	either statically or dynamically; added missing #include <string.h>
+	in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+	type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+	unsigned in ANSI C, signed in traditional"; made test program
+	self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER	/* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+	a = pms->abcd[0], b = pms->abcd[1],
+	c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+	/*
+	 * Determine dynamically whether this is a big-endian or
+	 * little-endian machine, since we can use a more efficient
+	 * algorithm on the latter.
+	 */
+	static const int w = 1;
+
+	if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0		/* little-endian */
+	{
+	    /*
+	     * On little-endian machines, we can process properly aligned
+	     * data without copying it.
+	     */
+	    if (!((data - (const md5_byte_t *)0) & 3)) {
+		/* data are properly aligned */
+		X = (const md5_word_t *)data;
+	    } else {
+		/* not aligned */
+		memcpy(xbuf, data, 64);
+		X = xbuf;
+	    }
+	}
+#endif
+#if BYTE_ORDER == 0
+	else			/* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0		/* big-endian */
+	{
+	    /*
+	     * On big-endian machines, we must arrange the bytes in the
+	     * right order.
+	     */
+	    const md5_byte_t *xp = data;
+	    int i;
+
+#  if BYTE_ORDER == 0
+	    X = xbuf;		/* (dynamic only) */
+#  else
+#    define xbuf X		/* (static only) */
+#  endif
+	    for (i = 0; i < 16; ++i, xp += 4)
+		xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+	}
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+	return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+	pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+	memcpy(pms->buf + offset, p, copy);
+	if (offset + copy < 64)
+	    return;
+	p += copy;
+	left -= copy;
+	md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+	md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+	memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
Index: vtkvis/tags/1.8.4/md5.h
===================================================================
--- vtkvis/tags/1.8.4/md5.h	(revision 6184)
+++ vtkvis/tags/1.8.4/md5.h	(revision 6184)
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+	references to Ghostscript; clarified derivation from RFC 1321;
+	now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+	added conditionalization for C++ compilation from Martin
+	Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];	/* message length in bits, lsw first */
+    md5_word_t abcd[4];		/* digest buffer */
+    md5_byte_t buf[64];		/* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
Index: vtkvis/tags/1.8.4/protocol.txt
===================================================================
--- vtkvis/tags/1.8.4/protocol.txt	(revision 6184)
+++ vtkvis/tags/1.8.4/protocol.txt	(revision 6184)
@@ -0,0 +1,1025 @@
+================================================================================
+================================================================================
+Protocol for vtkvis render server.  
+
+Commands take the general form of command and arguments using Tcl syntax and are
+terminated by a newline.  Some command arguments expect a Tcl list which can be
+sent by enclosing the list in curly braces to prevent the interpreter from 
+splitting the list into multiple arguments.  Likewise, strings with spaces 
+should be quoted or enclosed in curly braces.  In the documentation below,
+arguments are indicated by angle brackets and optional arguments are surrounded
+by question marks -- the brackets and question marks are not part of the
+protocol and should not be included in argument strings.  The arguments shown 
+in angle brackets should be replaced with a string (where there are fixed 
+choices this document indicates valid values by separating them with pipe 
+symbols), a list in Tcl syntax, or a numeric value.  Boolean arguments can take
+the form of 0|1, true|false, or yes|no.  Since the protocol commands are 
+executed in a Tcl interpreter, Tcl code such as math expressions in expr 
+commands enclosed in square brackets are also allowed.  Since the interpreter
+is a "safe" interpreter, some Tcl commands that could cause security issues may 
+not be available for use.
+
+When binary data is to be sent, it should follow the newline after a command 
+that indicates incoming data including a byte count argument.
+================================================================================
+================================================================================
+Requests:
+================================================================================
+
+== General dataset, scene and renderer commands ==
+
+axis autobounds <axis> <bool>
+     Toggle between automatic and explicit bounds settings.  To set an explicit
+     range use the 'axis bounds...' command.
+     <axis> = x|y|z|all
+axis autorange <axis> <bool>
+     Toggle between automatic and explicit range settings.  To set an explicit
+     range use the 'axis range...' command.  For scaling based on axis bounds,
+     see 'axis scale...'
+     <axis> = x|y|z|all
+axis bounds <axis> <min> <max>
+     Explicitly specify bounds to use for an axis.
+     <axis> = x|y|z|all
+axis color <axis> <r> <g> <b> <?opacity?>
+     Set color of axis lines, labels, titles, ticks
+     <axis> = x|y|z|all
+axis exp <xPower> <yPower> <zPower> <?useCustom?>
+     Control power scaling of labels.  When an exponent is set, the labels
+     are adjusted to show mantissa and the exponent is indicated in the title.
+     <xPower> = Base 10 exponent for X axis
+     <yPower> = Base 10 exponent for Y axis
+     <zPower> = Base 10 exponent for Z axis
+     <useCustom> = Flag to control if custom/manual powers are used.  Setting
+     this to off will re-enable automatic power scaling.
+axis flymode <mode>
+     <mode> = static_edges|static_triad|outer_edges|furthest_triad|closest_triad
+axis fontsz <fontPixelSize>
+     Controls size of labels and text in 3D mode
+axis gpcolor <axis> <r> <g> <b> <?opacity?>
+     Set color/opacity of inner grid polygons
+     <axis> = x|y|z|all
+axis gpolys <axis> <bool>
+     Turn on/off inner grid polygons/planes
+     <axis> = x|y|z|all
+axis grcolor <axis> <r> <g> <b> <?opacity?>
+     Set color/opacity of gridlines
+     <axis> = x|y|z|all
+axis grid <axis> <bool>
+     Turn on/off outer gridlines
+     <axis> = x|y|z|all
+axis igcolor <axis> <r> <g> <b> <?opacity?>
+     Set color/opacity of inner gridlines
+     <axis> = x|y|z|all
+axis igrid <axis> <bool>
+     Turn on/off inner gridlines
+     <axis> = x|y|z|all
+axis labels <axis> <bool>
+     Toggle visibility of axis labels
+     <axis> = x|y|z|all
+axis lcolor <axis> <r> <g> <b> <?opacity?>
+     Set color/opacity of tick labels
+     <axis> = x|y|z|all
+axis lfont <axis> <fontName>
+     Set label font family
+     <axis> = x|y|z|all
+     <fontName> = Arial|Courier|Times
+axis lformat <axis> <formatString>
+     <axis> = x|y|z|all
+     <formatString> = printf style format string
+axis lfsize <axis> <fontSizePts>
+     Set font size of labels in 2D mode - currently relies on patched VTK in 
+     runtime
+     <axis> = x|y|z|all
+     <fontSizePts> = Font size in points
+axis linecolor <axis> <r> <g> <b> <?opacity?>
+     Set color/opacity of main axis lines
+     <axis> = x|y|z|all
+axis lrot <axis> <rotation>
+     <axis> = x|y|z|all
+     <rotation> = rotation angle in degrees
+axis lscale <axis> <boolAuto> <xpow> <ypow> <zpow>
+     Control (auto-)scaling of labels with powers of 10
+     <axis> = x|y|z|all
+     <boolAuto> = Enable/disable automatic scaling with powers of 10
+     <xpow> = Explicitly set power on X axis
+     <ypow> = Explicitly set power on Y axis
+     <zpow> = Explicitly set power on Z axis
+axis minticks <axis> <bool>
+     Toggle visibility of axis minor tick marks
+     <axis> = x|y|z|all
+axis name <axis> <title>
+     <axis> = x|y|z|all
+     <title> = Title of axis
+axis origin <x> <y> <z> <?useCustom?>
+     <x>,<y>,<z> = point inside world coordinate bounds of axes box where axes 
+     should intersect
+     <useCustom> = Set to false to disable use of custom origin and re-enable 
+     automatic origin
+axis range <axis> <min> <max>
+     Explicitly set min,max range of axis labels.  Note that the min and max 
+     values themselves may not be given labels if they don't fall on tick 
+     divisions -- the min/max determine the value of the axis endpoints.  This
+     setting allows labeling axes independently of the world coordinate bounds.
+     <axis> = x|y|z|all
+     <min> = minimum value of axis for labels
+     <max> = maximum value of axis for labels
+axis scale <axis> <scale>
+     Set axis range to be computed based on scaling the axis bounds by the given
+     scale factor.
+     <axis> = x|y|z|all
+     <scale> = scale factor where range = bounds * scale
+axis tcolor <axis> <r> <g> <b> <?opacity?>
+     Set color/opacity of axis title
+     <axis> = x|y|z|all
+axis tickpos <position>
+     Set position of ticks on 3D axes (not implemented for 2D axes)
+     <position> = inside|outside|both
+axis ticks <axis> <bool>
+     Toggle visibility of axis tick marks
+     <axis> = x|y|z|all
+axis tfont <axis> <fontName>
+     Set title font family
+     <axis> = x|y|z|all
+     <fontName> = Arial|Courier|Times
+axis tfsize <axis> <fontSizePts>
+     Set font size of title in 2D mode - currently relies on patched VTK in 
+     runtime
+     <axis> = x|y|z|all
+     <fontSizePts> = Font size in points
+axis trot <axis> <rotation>
+     Currently a no-op -- needs a fix in vtkCubeAxesActor
+     <axis> = x|y|z|all
+     <rotation> = rotation angle in degrees
+axis units <axis> <units>
+     Currently only supported when camera mode is not image mode
+axis visible <axis> <bool>
+     <axis> = x|y|z|all
+     Note: 'all' means all enabled axes.  To force all axes on, set each axis on
+     individually -- however, it is best to let the server determine which of 
+     the individual axes are enabled based on the dataset bounds.
+
+camera aspect <aspectMode>
+       Set 2D aspect ratio mode
+       <aspectMode> = native|square|window
+camera get
+       Request current camera parameters
+camera mode <mode>
+       <mode> = persp|ortho|image
+camera orient <quatW> <quatX> <quatY> <quatZ>
+       Set scene orientation using a quaternion
+camera ortho <coordMode> <x> <y> <width> <height>
+       <coordMode> = pixel|world
+       Supply bounds of plot area for image camera mode
+camera pan <x> <y>
+       <x,y> viewport coordinates (window center at 0,0).  Positive x pan
+       means pan object to right (camera to left).  Positive y pan means
+       pan object down (camera up).  For example a pan of 0.5, 0.5 would
+       move the object center to the lower right corner of the window
+camera reset <?all?>
+       Option all resets orientation/rotation as well as pan/zoom/clip range
+camera rotate <yaw> <pitch> <roll>
+       Specify relative rotation in Euler angles
+camera set <posX> <posY> <posZ> <focalPtX> <focalPtY> <focalPtZ> <viewUpX> <viewUpY> <viewUpZ>
+       Set camera parameters: camera position, focal point and view up vector
+camera zoom <z>
+       Specify zoom ratio.  z > 1 is a zoom in, z < 1 is zoom out. z = 1
+       resets to default.
+
+clientinfo <list>
+
+colormap add <colorMapName> <colorMap> <opacityMap>
+         (Re-)define a colormap.  If colorMapName doesn't exist, it is created,
+         otherwise the colormap is redefined and all users will be updated.
+	 colorMap = Tcl list of {value r g b} control points
+         opacityMap = Tcl list of {value alpha} control points
+colormap define <colorMapName> <colorMap> <opacityMap>
+         (Re-)define a colormap (same as 'add')
+colormap delete <?colorMapName?>
+colormap res <numberOfTableEntries>
+         Set the "resolution" or number of lookup table entries in the colormap
+         <numberOfTableEntries> = integer or "default"
+
+dataset add <datasetName> data follows <nbytes>
+dataset delete <?datasetName?>
+dataset getscalar world <x> <y> <z> <datasetName>
+dataset getscalar pixel <x> <y> <datasetName>
+dataset getvector world <x> <y> <z> <datasetName>
+dataset getvector pixel <x> <y> <datasetName>
+        Use pixel for image camera mode
+dataset maprange <val> <?min?> <?max?> <?fieldName?> <?fieldType?> <?numComp?> <?component?>
+        Controls if data range for colormapping and contours is based on
+        cumulative range of all datasets ("all"), only visible datasets
+        ("visible") or each individual dataset ("separate").  Defaults to
+        "all"
+        <val> = all|explicit|visible|separate
+        The following arguments are only used for 'explicit' mode:
+        <min>,<max> = Required in explicit mode, specify the range to use
+        <fieldName> = Required in explicit mode, specify the field to which the
+        range should be applied
+        <fieldType> = point_data|cell_data|field_data, optional (defaults to 
+        point_data)
+        <numComp> = Number of components (1 = scalar, 3 = vector), optional (1
+        is default)
+        <component> = Component index for vector fields, required if numComp is
+        3, otherwise unused.
+dataset names
+        Returns a list of the loaded data sets
+dataset opacity <val> <?datasetName?>
+dataset scalar <scalarName> <?datasetName?>
+        Set the active scalar field to plot
+dataset vector <vectorName> <?datasetName?>
+        Set the active vector field to plot
+dataset visible <bool> <?datasetName?>
+
+legend <colormapName> <legendType> <fieldName> <legendTitle> <width> <height> <numLabels> <?datasetName?>
+       Causes legend to be rendered and written back with labels and title
+       (title may be left blank)
+       <legendType> = scalar|vmag|vx|vy|vz
+       <numLabels> Can be zero or more 'tick' value labels
+       If datasetName is omitted, the cumulative data range of all data sets 
+       will be used to label the legend -- otherwise, the current setting of 
+       'dataset maprange' will be used to determine if the individual dataset
+       range or cumulative range will be used.
+
+legend2 <colormapName> <width> <height>
+        Simple legend command for rendering legend without labels or title
+
+renderer clipplane <axis> <ratio> <direction>
+         Set a user clipping plane, ratio is [0,1] and is always interpreted as
+         0 = min_bound, 1 = max_bound, regardless of direction.  Sign of 
+         direction determines normal of clipping plane.  User clip planes do not
+         apply if camera mode is image.
+renderer depthpeel <bool> <?occlusionRatio?> <?maxPeels?>
+         Set use of depth peeling algorithm for transparency
+         <occlusionRatio> = [0,0.5] Value of 0 means exact rendering, higher
+         values can speed rendering at the cost of some quality.  From VTK docs:
+         "the threshold under which the algorithm stops to iterate over peel 
+         layers. This is the ratio of the number of pixels that have been 
+         touched by the last layer over the total number of pixels of the 
+         viewport area."
+         <maxPeels> = 0 means unlimited passes, positive value limits the number
+         of rendering passes 
+renderer light2side <bool>
+         Toggle use of two-sided lighting (controls if backfaces are lit with a
+         flipped normal)
+renderer lights <lightNum> <bool>
+         Toggle lights on/off.  Light 0 is the headlight, light 1 the skylight
+renderer render
+         Force a new image to be rendered - use for advancing animation
+
+screen bgcolor <r> <g> <b>
+screen size <width> <height>
+
+== Graphics objects ==
+
+arc add <centerX> <centerY> <centerZ> <startX> <startY> <startZ> <normX> <normY> <normZ> <angle> <name>
+    Creates a circular arc given a center and starting point, a normal and a 
+    sweep angle.
+arc color <r> <g> <b> <?name?>
+arc delete <?name?>
+arc linecolor <r> <g> <b> <?name?>
+arc linewidth <val> <?name?>
+arc opacity <val> <?name?>
+arc orient <qw> <qx> <qy> <qz> <?name?>
+arc origin <x> <y> <z> <?name?>
+arc pos <x> <y> <z> <?name?>
+arc resolution <res> <?name?>
+arc scale <sx> <sy> <sz> <?name?>
+arc visible <bool> <?name?>
+
+arrow add <tipRadius> <shaftRadius> <tipLength> <name>
+      Arrow will have base at 0,0,0 and tip at 1, 0, 0
+arrow color <r> <g> <b> <?name?>
+arrow culling <bool> <?name?>
+arrow delete <?name?>
+arrow edges <bool> <?name?>
+arrow flipnorms <bool> <?name?>
+arrow lighting <bool> <?name?>
+arrow linecolor <r> <g> <b> <?name?>
+arrow linewidth <val> <?name?>
+arrow material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+arrow opacity <val> <?name?>
+arrow orient <qw> <qx> <qy> <qz> <?name?>
+arrow origin <x> <y> <z> <?name?>
+arrow pos <x> <y> <z> <?name?>
+arrow resolution <tipRes> <shaftRes> <?name?>
+arrow scale <sx> <sy> <sz> <?name?>
+arrow shading <val> <?name?>
+      val = flat|smooth
+arrow visible <bool> <?name?>
+arrow wireframe <bool> <?name?>
+
+box add <xLen> <yLen> <zLen> <name>
+box color <r> <g> <b> <?name?>
+box culling <bool> <?name?>
+box delete <?name?>
+box edges <bool> <?name?>
+box flipnorms <bool> <?name?>
+box lighting <bool> <?name?>
+box linecolor <r> <g> <b> <?name?>
+box linewidth <val> <?name?>
+box material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+box opacity <val> <?name?>
+box orient <qw> <qx> <qy> <qz> <?name?>
+box origin <x> <y> <z> <?name?>
+box pos <x> <y> <z> <?name?>
+box scale <sx> <sy> <sz> <?name?>
+box shading <val> <?name?>
+    val = flat|smooth
+box visible <bool> <?name?>
+box wireframe <bool> <?name?>
+
+cone add <radius> <height> <cap> <name>
+     <cap> = boolean flag for cap disks
+cone color <r> <g> <b> <?name?>
+cone culling <bool> <?name?>
+cone delete <?name?>
+cone edges <bool> <?name?>
+cone flipnorms <bool> <?name?>
+cone lighting <bool> <?name?>
+cone linecolor <r> <g> <b> <?name?>
+cone linewidth <val> <?name?>
+cone material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+cone opacity <val> <?name?>
+cone orient <qw> <qx> <qy> <qz> <?name?>
+cone origin <x> <y> <z> <?name?>
+cone pos <x> <y> <z> <?name?>
+cone resolution <res> <?name?>
+cone scale <sx> <sy> <sz> <?name?>
+cone shading <val> <?name?>
+     val = flat|smooth
+cone visible <bool> <?name?>
+cone wireframe <bool> <?name?>
+
+contour2d add numcontours <n> <?datasetName?>
+          Generate evenly spaced contours including range endpoints.  See also
+          'dataset maprange' command.
+contour2d add contourlist <list> <?datasetName?>
+	  list = {isoval1 isoval2 isoval3...}
+contour2d color <r> <g> <b> <?datasetName?>
+          synonym for linecolor
+contour2d colormap <colormapName> <?datasetName?>
+contour2d colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+          Set the field used to color the object.  'constant' means to use
+          the constant color defined by the color subcommand.  'scalar' uses
+          the active scalar field.  'vmag' uses the magnitude of the current
+          vector field, and 'vx','vy','vz' use the corresponding component of
+          the active vector field.
+contour2d contourlist <list> <?dataSetName?>
+contour2d delete <?datasetName?>
+contour2d lighting <bool> <?datasetName?>
+contour2d linecolor <r> <g> <b> <?datasetName?>
+          synonym for color
+contour2d linewidth <val> <?datasetName?>
+contour2d numcontours <n> <?dataSetName?>
+contour2d opacity <val> <?datasetName?>
+contour2d orient <qw> <qx> <qy> <qz> <?dataSetName?>
+contour2d pos <x> <y> <z> <?dataSetName?>
+contour2d scale <sx> <sy> <sz> <?dataSetName?>
+contour2d visible <bool> <?datasetName?>
+
+contour3d add numcontours <n> <?datasetName?>
+          Generate evenly spaced contours including range endpoints.  See also
+          'dataset maprange' command.
+contour3d add contourlist <list> <?datasetName?>
+	  list = {isoval1 isoval2 isoval3...}
+contour3d color r g b <?datasetName?>
+contour3d colormap <colorMapName> <?dataSetName?>
+contour3d colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+          Set the field used to color the object.  'constant' means to use
+          the constant color defined by the color subcommand.  'scalar' uses
+          the active scalar field.  'vmag' uses the magnitude of the current
+          vector field, and 'vx','vy','vz' use the corresponding component of
+          the active vector field.
+contour3d contourlist <list> <?dataSetName?>
+contour3d delete <?datasetName?>
+contour3d edges <bool> <?datasetName?>
+contour3d lighting <bool> <?datasetName?>
+contour3d linecolor <r> <g> <b> <?datasetName?>
+contour3d linewidth <val> <?datasetName?>
+contour3d numcontours <n> <?dataSetName?>
+contour3d opacity <val> <?datasetName?>
+contour3d orient <qw> <qx> <qy> <qz> <?dataSetName?>
+contour3d pos <x> <y> <z> <?dataSetName?>
+contour3d scale <sx> <sy> <sz> <?dataSetName?>
+contour3d visible <bool> <?datasetName?>
+contour3d wireframe <bool> <?datasetName?>
+
+cutplane add <?dataSetName?>
+cutplane axis <axis> <bool> <?dataSetName?>
+         Toggle visibility of the 3 principal axis cutplanes
+cutplane cloudstyle <style> <?datasetName?>
+         <style> = mesh | splat
+cutplane color r g b <?datasetName?>
+         Set color of outline bounding box
+cutplane colormap <colorMapName> <?dataSetName?>
+cutplane colormode <scalar|vmag|vx|vy|vz> <fieldName> <?datasetName?>
+         Set the field used to color the object.  'scalar' uses
+         the active scalar field.  'vmag' uses the magnitude of the current
+         vector field, and 'vx','vy','vz' use the corresponding component of
+         the active vector field.
+cutplane delete <?dataSetName?>
+cutplane edges <bool> <?dataSetName?>
+cutplane lighting <bool> <?dataSetName?>
+cutplane linecolor <r> <g> <b> <?dataSetName?>
+cutplane linewidth <width> <?dataSetName?>
+cutplane opacity <value> <?dataSetName?>
+cutplane orient <qw> <qx> <qy> <qz> <?dataSetName?>
+cutplane outline <bool> <?dataSetName?>
+         Toggle rendering of cutplane outlines
+cutplane pos <x> <y> <z> <?dataSetName?>
+cutplane preinterp <bool> <?dataSetName?>
+         Controls if VTK's InterpolateScalarsBeforeMapping option is set.
+         Setting this on will give more correct colors, as the interpolation
+         is done on texture coordinates that lookup into a 1D texture instead
+         of using color interpolation from triangle vertices.
+cutplane scale <sx> <sy> <sz> <?dataSetName?>
+cutplane slice axis ratio <?dataSetName?>
+         For 3D data, select a slice along a principle axis of the volume. The
+         ratio is [0,1]
+cutplane visible <bool> <?dataSetName?>
+cutplane wireframe <bool> <?datasetName?>
+
+cylinder add <radius> <height> <cap> <name>
+         <cap> = boolean flag for cap disks
+cylinder color <r> <g> <b> <?name?>
+cylinder culling <bool> <?name?>
+cylinder delete <?name?>
+cylinder edges <bool> <?name?>
+cylinder flipnorms <bool> <?name?>
+cylinder lighting <bool> <?name?>
+cylinder linecolor <r> <g> <b> <?name?>
+cylinder linewidth <val> <?name?>
+cylinder material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+cylinder opacity <val> <?name?>
+cylinder orient <qw> <qx> <qy> <qz> <?name?>
+cylinder origin <x> <y> <z> <?name?>
+cylinder pos <x> <y> <z> <?name?>
+cylinder resolution <res> <?name?>
+cylinder scale <sx> <sy> <sz> <?name?>
+cylinder shading <val> <?name?>
+         val = flat|smooth
+cylinder visible <bool> <?name?>
+cylinder wireframe <bool> <?name?>
+
+disk add <innerRadius> <outerRadius> <name>
+disk color <r> <g> <b> <?name?>
+disk culling <bool> <?name?>
+disk delete <?name?>
+disk edges <bool> <?name?>
+disk flipnorms <bool> <?name?>
+disk lighting <bool> <?name?>
+disk linecolor <r> <g> <b> <?name?>
+disk linewidth <val> <?name?>
+disk material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+disk opacity <val> <?name?>
+disk orient <qw> <qx> <qy> <qz> <?name?>
+disk origin <x> <y> <z> <?name?>
+disk pos <x> <y> <z> <?name?>
+disk resolution <resRadial> <resCircum> <?name?>
+disk scale <sx> <sy> <sz> <?name?>
+disk shading <val> <?name?>
+     val = flat|smooth
+disk visible <bool> <?name?>
+disk wireframe <bool> <?name?>
+
+glyphs add <shape> <?dataSetName?>
+       See 'glyphs shape' for list of shapes
+glyphs color r g b <?datasetName?>
+glyphs colormap <colorMapName> <?dataSetName?>
+glyphs colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?dataSetName?>
+       Set the color mode: color by scalar field or
+       vector magnitude -- uses the current color map,
+       or color for constant color
+glyphs delete <?dataSetName?>
+glyphs edges <bool> <?datasetName?>
+glyphs gorient <bool> <fieldName> <?datasetName?>
+       Set the glyph orientation mode: orient glyphs by
+       a named vector field, or set to default orientation (off)
+glyphs gscale <scaleFactor> <?datasetName?>
+       Set glyph scaling factor
+glyphs lighting <bool> <?datasetName?>
+glyphs linecolor <r> <g> <b> <?datasetName?>
+glyphs linewidth <val> <?datasetName?>
+glyphs normscale <bool> <?datasetName?>
+       Control if data values are normalized to [0,1] range before applying
+       glyph scaling factor (gscale)
+glyphs numglyphs <max> <random> <offset> <ratio> <?dataSetName?>
+       Limit the maximum number of glyphs displayed.  Can use either a random
+       sampling or every nth point sampling (useful for uniform grids)
+       <max> = integer maximum number of glyphs
+       <random> = bool indicating if random sampling should be used
+       <offset> = offset point index from which to start sampling
+       <ratio> = If random is false, use this to control every nth point sampling
+glyphs opacity <val> <?datasetName?>
+glyphs orient <qw> <qx> <qy> <qz> <?dataSetName?>
+glyphs pos <x> <y> <z> <?dataSetName?>
+glyphs ptsize <size> <?dataSetName?>
+glyphs quality <val> <?dataSetName?>
+       Set glyph shape resolution quality val=[0,10], 1 is default
+glyphs scale <sx> <sy> <sz> <?dataSetName?>
+glyphs shape <arrow|cone|cube|cylinder|dodecahedron|icosahedron|line|octahedron|point|sphere|tetrahedron> <?datasetName?>
+glyphs smode <scalar|vmag|vcomp|off> <fieldName> <?dataSetName?>
+       Set the scaling mode: use the scalar field, vector magnitude, 
+       vector components, or disable scaling
+glyphs visible <bool> <?datasetName?>
+glyphs wireframe <bool> <?datasetName?>
+
+group add <nodeList> <name>
+      Add listed nodes to group named <name>.  Group will be created if
+      it doesn't exist, but nodes may be added to existing groups.  The
+      nodes will be removed from the virtual root scene.
+group delete <?name?>
+group orient <qw> <qx> <qy> <qz> <?name?>
+group origin <x> <y> <z> <?name?>
+group pos <x> <y> <z> <?name?>
+group remove <nodeList> <name>
+      Remove listed nodes from group.  Currently, nodes will be added back
+      to the virtual root scene, and so nodes must be explicitly deleted to 
+      remove them from the scene.
+group scale <sx> <sy> <sz> <?name?>
+group visible <bool> <?name?>
+
+heightmap add numcontours <n> <heightScale> <?dataSetName?>
+          Generate evenly spaced contours including range endpoints.  See also
+          'dataset maprange' command.
+heightmap add contourlist <list> <heightScale> <?dataSetName?>
+	  list = {isoval1 isoval2 isoval3...}
+heightmap aspect <aspectRatio>
+          Set scaling by aspect ratio
+          <aspectRatio> = width/height or 0 for native aspect
+heightmap cloudstyle <style> <?datasetName?>
+          <style> = mesh | splat
+heightmap colormap <colorMapName> <?dataSetName?>
+heightmap colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+          Set the field used to color the object.  'constant' means to use
+          the constant color defined by the color subcommand.  'scalar' uses
+          the active scalar field.  'vmag' uses the magnitude of the current
+          vector field, and 'vx','vy','vz' use the corresponding component of
+          the active vector field.
+heightmap colormode2 <scalar|vmag|vx|vy|vz|constant> <fieldName> <point_data|cell_data|field_data|any> <min> <max> <?datasetName?>
+          Set the field used to color the object.  'constant' means to use
+          the constant color defined by the color subcommand.  'scalar' uses
+          the active scalar field.  'vmag' uses the magnitude of the current
+          vector field, and 'vx','vy','vz' use the corresponding component of
+          the active vector field.
+heightmap contourlist <list> <?dataSetName?>
+heightmap delete <?dataSetName?>
+heightmap edges <bool> <?dataSetName?>
+heightmap heightscale <value> <?dataSetName?>
+heightmap isolinecmap <bool> <?dataSetName?>
+          Toggle colormapping of isolines (false means to use constant color)
+heightmap isolinecolor <r> <g> <b> <?dataSetName?>
+heightmap isolines <bool> <?dataSetName?>
+heightmap isolinewidth <width> <?dataSetName?>
+heightmap lighting <bool> <?dataSetName?>
+heightmap linecolor <r> <g> <b> <?dataSetName?>
+heightmap linewidth <width> <?dataSetName?>
+heightmap numcontours <n> <?dataSetName?>
+heightmap opacity <value> <?dataSetName?>
+heightmap orient <qw> <qx> <qy> <qz> <?dataSetName?>
+heightmap pos <x> <y> <z> <?dataSetName?>
+heightmap preinterp <bool> <?dataSetName?>
+          Controls if VTK's InterpolateScalarsBeforeMapping option is set.
+          Setting this on will give more correct colors, as the interpolation
+          is done on texture coordinates that lookup into a 1D texture instead
+          of using color interpolation from triangle vertices.
+heightmap scale <sx> <sy> <sz> <?dataSetName?>
+heightmap slice <axis> <ratio> <?dataSetName?>
+          For 3D data, select a slice along a principle axis of the volume. The
+          ratio is [0,1]
+heightmap surface <bool> <?dataSetName?>
+          Toggle rendering of colormapped surface (mountain plot or cutplane)
+heightmap visible <bool> <?dataSetName?>
+heightmap wireframe <bool> <?datasetName?>
+
+image add <?dataSetName?>
+      Create an image from a vtkImageData data set
+image backing <bool> <?dataSetName?>
+image bg <bool> <?dataSetName?>
+image border <bool> <?dataSetName?>
+image color <r> <g> <b> <?dataSetName?>
+image colormap <colorMapName> <?dataSetName?>
+image delete <?dataSetName?>
+image extents <xmin> <xmax> <ymin> <ymax> <zmin> <zmax> <?dataSetName?>
+      Deprecated
+image follow <bool> <?dataSetName?>
+      Toggle camera following.  When on, slice plane will face the camera and
+      camera focal point determines slice offset
+image level <level> <?dataSetName?>
+image opacity <opacity> <?dataSetName?>
+image orient <qw> <qx> <qy> <qz> <?dataSetName?>
+image pos <x> <y> <z> <?dataSetName?>
+image scale <sx> <sy> <sz> <?dataSetName?>
+image slice <nx> <ny> <nz> <ox> <oy> <oz> <?dataSetName?>
+      Disable camera follow and set slice plane by plane normal (nx,ny,nz) and 
+      origin (ox,oy,oz)
+image visible <bool> <?dataSetName?>
+image window <windowWidth> <?dataSetName?>
+image zslice <sliceNum> <?dataSetName?>
+      Deprecated
+
+imgcutplane add <?dataSetName?>
+imgcutplane axis <axis> <bool> <?dataSetName?>
+            Toggle visibility of the 3 principal axis cutplanes
+imgcutplane color r g b <?datasetName?>
+            Set color of outline bounding box
+imgcutplane colormap <colorMapName> <?dataSetName?>
+imgcutplane delete <?dataSetName?>
+imgcutplane level <level> <?dataSetName?>
+imgcutplane material <ambient> <diffuse> <?dataSetName?>
+imgcutplane opacity <value> <?dataSetName?>
+imgcutplane orient <qw> <qx> <qy> <qz> <?dataSetName?>
+imgcutplane outline <bool> <?dataSetName?>
+            Toggle rendering of imgcutplane outlines
+imgcutplane pos <x> <y> <z> <?dataSetName?>
+imgcutplane scale <sx> <sy> <sz> <?dataSetName?>
+imgcutplane slice axis ratio <?dataSetName?>
+            For 3D data, select a slice along a principle axis of the volume. The
+            ratio is [0,1]
+imgcutplane visible <bool> <?dataSetName?>
+imgcutplane window <window> <?dataSetName?>
+
+lic add <?datasetName?>
+lic colormap <colormapName> <?datasetName?>
+lic delete <?datasetName?>
+lic edges <bool> <?datasetName?>
+lic lighting <bool> <?datasetName?>
+lic linecolor <r> <g> <b> <?datasetName?>
+lic linewidth <val> <?datasetName?>
+lic opacity <val> <?datasetName?>
+lic orient <qw> <qx> <qy> <qz> <?dataSetName?>
+lic pos <x> <y> <z> <?dataSetName?>
+lic preinterp <bool> <?dataSetName?>
+    Controls if VTK's InterpolateScalarsBeforeMapping option is set.
+    Setting this on will give more correct colors, as the interpolation
+    is done on texture coordinates that lookup into a 1D texture instead
+    of using color interpolation from triangle vertices.
+lic scale <sx> <sy> <sz> <?dataSetName?>
+lic slice <axis> <ratio> <?datasetName?>
+    For 3D data, select a slice along a principle axis of the volume. The
+    ratio is [0,1]
+lic visible <bool> <?datasetName?>
+
+line add <points> <name>
+     Create a polyline from the list of point coordinates in <points>. The
+     list size must be a multiple of 3 (each point's x,y,z coordinates)
+line color <r> <g> <b> <?name?>
+line delete <?name?>
+line linecolor <r> <g> <b> <?name?>
+     Synonym for color
+line linewidth <val> <?name?>
+line opacity <val> <?name?>
+line orient <qw> <qx> <qy> <qz> <?name?>
+line origin <x> <y> <z> <?name?>
+line pos <x> <y> <z> <?name?>
+line scale <sx> <sy> <sz> <?name?>
+line visible <bool> <?name?>
+
+molecule add <?datasetName?>
+molecule aquality <val> <?datasetName?>
+         Set atom sphere resolution quality val=[0,10], 1 is default
+molecule ascale <val> <?datasetName?>
+         Set atom scale factor
+molecule atoms <bool> <?datasetName?>
+         Toggle rendering of atoms
+molecule bcmode <by_elements|constant> <?datasetName?>
+         Set bond color mode
+molecule bcolor <r> <g> <b> <?datasetName?>
+         Set constant bond color
+molecule bonds <bool> <?datasetName?>
+         Toggle rendering of bonds
+molecule bquality <val> <?datasetName?>
+         Set bond cylinder resolution quality val=[0,10], 1 is default
+molecule bscale <val> <?datasetName?>
+         Set bond scale factor
+molecule bstyle <cylinder|line> <?datasetName?>
+         Set bond render style
+molecule color r g b <?datasetName?>
+molecule colormap <colormapName> <?datasetName?>
+molecule colormode <by_elements|scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+         Set the field used to color the object.  'constant' means to use
+         the constant color defined by the color subcommand.  'scalar' uses
+         the active scalar field.  'vmag' uses the magnitude of the current
+         vector field, and 'vx','vy','vz' use the corresponding component of
+         the active vector field.
+molecule delete <?datasetName?>
+molecule edges <bool> <?datasetName?>
+molecule labels <bool> <?datasetName?>
+         Toggle display of atom labels
+molecule lfield <fieldName> <?datasetName?>
+         Set the field used for atom labels
+molecule lighting <bool> <?datasetName?>
+molecule linecolor <r> <g> <b> <?datasetName?>
+molecule linewidth <val> <?datasetName?>
+molecule material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?datasetName?>
+molecule opacity <val> <?datasetName?>
+molecule orient <qw> <qx> <qy> <qz> <?dataSetName?>
+molecule pos <x> <y> <z> <?dataSetName?>
+molecule rscale <val> <?dataSetName?>
+         Atom radius scaling
+         val = van_der_waals|covalent|atomic|none
+molecule scale <sx> <sy> <sz> <?dataSetName?>
+molecule visible <bool> <?datasetName?>
+molecule wireframe <bool> <?datasetName?>
+
+outline add <?datasetName?>
+outline color <r> <g> <b> <?datasetName?>
+outline delete <?datasetName?>
+outline linecolor <r> <g> <b> <?datasetName?>
+        Synonym for color
+outline linewidth <val> <?datasetName?>
+outline opacity <val> <?datasetName?>
+outline orient <qw> <qx> <qy> <qz> <?datasetName?>
+outline pos <x> <y> <z> <?datasetName?>
+outline scale <sx> <sy> <sz> <?datasetName?>
+outline visible <bool> <?datasetName?>
+
+parallelepiped add <v0x> <v0y> <v0z> <v1x> <v1y> <v1z> <v2x> <v2y> <v2z> <name>
+               Specify parallelepiped using 3 basis vectors.  The 3 vectors should
+               be given in order to create a right-handed coordinate system, i.e.
+               (v0 cross v1) dot v2 should be positive.
+parallelepiped color <r> <g> <b> <?name?>
+parallelepiped culling <bool> <?name?>
+parallelepiped delete <?name?>
+parallelepiped edges <bool> <?name?>
+parallelepiped flipnorms <bool> <?name?>
+parallelepiped lighting <bool> <?name?>
+parallelepiped linecolor <r> <g> <b> <?name?>
+parallelepiped linewidth <val> <?name?>
+parallelepiped material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+parallelepiped opacity <val> <?name?>
+parallelepiped orient <qw> <qx> <qy> <qz> <?name?>
+parallelepiped origin <x> <y> <z> <?name?>
+parallelepiped pos <x> <y> <z> <?name?>
+parallelepiped scale <sx> <sy> <sz> <?name?>
+parallelepiped shading <val> <?name?>
+               val = flat|smooth
+parallelepiped visible <bool> <?name?>
+parallelepiped wireframe <bool> <?name?>
+
+polydata add <?datasetName?>
+polydata cloudstyle <style> <?datasetName?>
+         <style> = mesh | points
+polydata color <r> <g> <b> <?datasetName?>
+polydata colormap <colormapName> <?datasetName?>
+polydata colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+         Set the field used to color the object.  'constant' means to use
+         the constant color defined by the color subcommand.  'scalar' uses
+         the active scalar field.  'vmag' uses the magnitude of the current
+         vector field, and 'vx','vy','vz' use the corresponding component of
+         the active vector field.
+polydata culling <bool> <?name?>
+polydata delete <?datasetName?>
+polydata edges <bool> <?datasetName?>
+polydata lighting <bool> <?datasetName?>
+polydata linecolor <r> <g> <b> <?datasetName?>
+polydata linewidth <val> <?datasetName?>
+polydata material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp>
+polydata opacity <val> <?datasetName?>
+polydata orient <qw> <qx> <qy> <qz> <?dataSetName?>
+polydata origin <x> <y> <z> <?name?>
+polydata pos <x> <y> <z> <?dataSetName?>
+polydata preinterp <bool> <?dataSetName?>
+         Controls if VTK's InterpolateScalarsBeforeMapping option is set.
+         Setting this on will give more correct colors, as the interpolation
+         is done on texture coordinates that lookup into a 1D texture instead
+         of using color interpolation from triangle vertices.
+polydata ptsize <size> <?dataSetName?>
+polydata scale <sx> <sy> <sz> <?dataSetName?>
+polydata shading <val> <?name?>
+         val = flat|smooth
+polydata visible <bool> <?datasetName?>
+polydata wireframe <bool> <?datasetName?>
+
+polygon add <numSides> <centerX> <centerY> <centerZ> <normX> <normY> <normZ> <radius> <name>
+polygon color <r> <g> <b> <?name?>
+polygon culling <bool> <?name?>
+polygon delete <?name?>
+polygon edges <bool> <?name?>
+polygon flipnorms <bool> <?name?>
+polygon lighting <bool> <?name?>
+polygon linecolor <r> <g> <b> <?name?>
+polygon linewidth <val> <?name?>
+polygon material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+polygon opacity <val> <?name?>
+polygon orient <qw> <qx> <qy> <qz> <?name?>
+polygon origin <x> <y> <z> <?name?>
+polygon pos <x> <y> <z> <?name?>
+polygon scale <sx> <sy> <sz> <?name?>
+polygon shading <val> <?name?>
+        val = flat|smooth
+polygon visible <bool> <?name?>
+polygon wireframe <bool> <?name?>
+
+pseudocolor add <?datasetName?>
+pseudocolor cloudstyle <style> <?datasetName?>
+            <style> = mesh | points | splat
+pseudocolor color <r> <g> <b> <?datasetName?>
+pseudocolor colormap <colormapName> <?datasetName?>
+pseudocolor colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+            Set the field used to color the object.  'constant' means to use
+            the constant color defined by the color subcommand.  'scalar' uses
+            the active scalar field.  'vmag' uses the magnitude of the current
+            vector field, and 'vx','vy','vz' use the corresponding component of
+            the active vector field.
+pseudocolor delete <?datasetName?>
+pseudocolor edges <bool> <?datasetName?>
+pseudocolor lighting <bool> <?datasetName?>
+pseudocolor linecolor <r> <g> <b> <?datasetName?>
+pseudocolor linewidth <val> <?datasetName?>
+pseudocolor opacity <val> <?datasetName?>
+pseudocolor orient <qw> <qx> <qy> <qz> <?dataSetName?>
+pseudocolor pos <x> <y> <z> <?dataSetName?>
+pseudocolor preinterp <bool> <?dataSetName?>
+            Controls if VTK's InterpolateScalarsBeforeMapping option is set.
+            Setting this on will give more correct colors, as the interpolation
+            is done on texture coordinates that lookup into a 1D texture instead
+            of using color interpolation from triangle vertices.
+pseudocolor ptsize <size> <?dataSetName?>
+pseudocolor scale <sx> <sy> <sz> <?dataSetName?>
+pseudocolor visible <bool> <?datasetName?>
+pseudocolor wireframe <bool> <?datasetName?>
+
+sphere add <centerX> <centerY> <centerZ> <radius> <name>
+sphere color <r> <g> <b> <?name?>
+sphere culling <bool> <?name?>
+sphere delete <?name?>
+sphere edges <bool> <?name?>
+sphere flipnorms <bool> <?name?>
+sphere lighting <bool> <?name?>
+sphere linecolor <r> <g> <b> <?name?>
+sphere linewidth <val> <?name?>
+sphere material <ambientCoeff> <diffuseCoeff> <specularCoeff> <specularExp> <?name?>
+sphere opacity <val> <?name?>
+sphere orient <qw> <qx> <qy> <qz> <?name?>
+sphere origin <x> <y> <z> <?name?>
+sphere pos <x> <y> <z> <?name?>
+sphere resolution <thetaRes> <phiRes> <?name?>
+sphere scale <sx> <sy> <sz> <?name?>
+sphere section <thetaStart> <thetaEnd> <phiStart> <phiEnd> <?name?>
+       Angles are in degrees
+sphere shading <val> <?name?>
+       val = flat|smooth
+sphere visible <bool> <?name?>
+sphere wireframe <bool> <?name?>
+
+streamlines add <?datasetName?>
+streamlines color <r> <g> <b> <?datasetName?>
+            Set the constant color of streamlines used for color mode 'constant'
+streamlines colormap <colormapName> <?datasetName?>
+            Colormap used to color streamlines/tubes/ribbons by vector magnitude
+streamlines colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+            Set the field used to color the streamlines.  'constant' means to use
+            the constant color defined by the color subcommand.  'scalar' uses
+            the active scalar field.  'vmag' uses the magnitude of the current
+            vector field, and 'vx','vy','vz' use the corresponding component of
+            the active vector field.
+streamlines delete <?datasetName?>
+streamlines edges <bool> <?datasetName?>
+            Turn on/off edges for tubes, ribbons
+streamlines length <length> <?datasetName?>
+            Set maximum length in world coordinates of streamlines
+streamlines lighting <bool> <?datasetName?>
+streamlines linecolor <r> <g> <b> <?datasetName?>
+            Set color of edges for tubes, ribbons (lines are colored by
+            colormap)
+streamlines lines <?datasetName?>
+            Set rendering type to polylines
+streamlines linewidth <val> <?datasetName?>
+streamlines opacity <val> <?datasetName?>
+streamlines orient <qw> <qx> <qy> <qz> <?dataSetName?>
+streamlines pos <x> <y> <z> <?dataSetName?>
+streamlines ribbons <width> <angle> <?datasetName?>
+            Set rendering type to ribbons, width is minimum half-width, angle is
+            degrees offset from normal orientation
+streamlines scale <sx> <sy> <sz> <?dataSetName?>
+streamlines seed color <r> <g> <b> <?datasetName?>
+streamlines seed disk <centerX> <centerY> <centerZ> <normalX> <normalY> <normalZ> <radius> <innerRadius> <numPoints> <?dataSetName?>
+            Create a disk seed area with optional hole, filled with randomly 
+            placed points
+streamlines seed fmesh <numPoints> data follows <nbytes> <?datasetName?>
+            Fill a mesh supplied as a VTK data file with randomly placed points
+streamlines seed fpoly <centerX> <centerY> <centerZ> <normalX> <normalY> <normalZ> <angle> <radius> <numSides> <numPoints> <?dataSetName?>
+            Create a regular n-sided polygonal seed area filled with randomly 
+            placed points
+streamlines seed mesh data follows <nbytes> <?datasetName?>
+            Use points of a mesh supplied as a VTK data file
+streamlines seed numpts <?datasetName?>
+            If seeds have been created using 'seed random', 'seed fmesh',
+            'seed mesh' or 'seed points' this command can be used to change the 
+            number of randomly distributed seeds.  In the case of 'seed mesh'
+            or 'seed points', the number is the maximum number of points of the 
+            mesh to be used -- the actual number may be fewer if the mesh
+            contains fewer points.  Set to a number less than 0 to use all the
+            points of the mesh.
+streamlines seed points <?datasetName?>
+            Use points of the streamlines' dataset mesh
+streamlines seed polygon <centerX> <centerY> <centerZ> <normalX> <normalY> <normalZ> <angle> <radius> <numSides> <?dataSetName?>
+            Create seed points from vertices of a regular n-sided polygon
+streamlines seed ptsize <size> <?datasetName?>
+            Set the point size for rendering seed points
+streamlines seed rake <startX> <startY> <startZ> <endX> <endY> <endZ> <numPoints> <?datasetName?>
+streamlines seed random <numPoints> <?datasetName?>
+            Fill the streamlines' dataset mesh with randomly placed points
+streamlines seed visible <bool> <?datasetName?>
+streamlines tubes <numSides> <radius> <?datasetName?>
+            Set rendering type to tubes, numSides is number of sides of tubes,
+            radius is minimum tube radius
+streamlines visible <bool> <?datasetName?>
+
+text3d add <textString> <name>
+text3d bold <bool> <?name?>
+text3d color <r> <g> <b> <?name?>
+text3d delete <?name?>
+text3d fntfamily <fontName> <?name?>
+text3d fntsize <fontSize> <?name?>
+text3d follow <bool> <?name?>
+       Toggle text automatically aligning toward camera
+text3d italic <bool> <?name?>
+text3d opacity <opacity> <?name?>
+text3d orient <qw> <qx> <qy> <qz> <?name?>
+text3d origin <x> <y> <z> <?name?>
+text3d pos <x> <y> <z> <?name?>
+text3d scale <sx> <sy> <sz> <?name?>
+text3d shadow <bool> <?name?>
+text3d text <text> <?name?>
+text3d visible <bool> <?name?>
+
+volume add <?datasetName?>
+volume blendmode <mode> <?dataSetName?>
+       mode = composite|max_intensity|min_intensity|additive
+volume colormap <colorMapName> <?datasetName?>
+volume delete <?datasetName?>
+volume lighting <bool> <?datasetName?>
+volume opacity <opacity> <?datasetName?>
+volume orient <qw> <qx> <qy> <qz> <?dataSetName?>
+volume pos <x> <y> <z> <?dataSetName?>
+volume quality <val> <?datasetName?>
+       val = [0,1]
+volume scale <sx> <sy> <sz> <?dataSetName?>
+volume shading ambient <coeff> <?datasetName?>
+volume shading diffuse <coeff> <?datasetName?>
+volume shading specular <coeff> <power> <?datasetName?>
+volume visible <bool> <?datasetName?>
+
+warp add <?dataSetName?>
+warp cloudstyle <style> <?datasetName?>
+     <style> = mesh | points
+warp color <r> <g> <b> <?datasetName?>
+warp colormap <colormapName> <?datasetName?>
+warp colormode <scalar|vmag|vx|vy|vz|constant> <fieldName> <?datasetName?>
+     Set the field used to color the object.  'constant' means to use
+     the constant color defined by the color subcommand.  'scalar' uses
+     the active scalar field.  'vmag' uses the magnitude of the current
+     vector field, and 'vx','vy','vz' use the corresponding component of
+     the active vector field.
+warp delete <?dataSetName?>
+warp edges <bool> <?dataSetName?>
+warp lighting <bool> <?dataSetName?>
+warp linecolor <r> <g> <b> <?dataSetName?>
+warp linewidth <width> <?dataSetName?>
+warp opacity <value> <?dataSetName?>
+warp orient <qw> <qx> <qy> <qz> <?dataSetName?>
+warp pos <x> <y> <z> <?dataSetName?>
+warp preinterp <bool> <?dataSetName?>
+     Controls if VTK's InterpolateScalarsBeforeMapping option is set.
+     Setting this on will give more correct colors, as the interpolation
+     is done on texture coordinates that lookup into a 1D texture instead
+     of using color interpolation from triangle vertices.
+warp ptsize <size> <?dataSetName?>
+warp scale <sx> <sy> <sz> <?dataSetName?>
+warp visible <bool> <?dataSetName?>
+warp warpscale <value> <?dataSetName?>
+     Specify amount to scale vector magnitudes when warping
+warp wireframe <bool> <?datasetName?>
+
+================================================================================
+Replies:
+================================================================================
+nv>camera set <posX> <posY> <posZ> <focalPtX> <focalPtY> <focalPtZ> <viewUpX> <viewUpY> <viewUpZ>
+   Reply to "camera get"
+nv>image -type image -bytes <nbytes>
+  <binary RGB data>
+nv>image -type image -bbox {x y w h} -bytes <nbytes>
+  <binary RGB data>
+  The bounding box of the 2D image camera zoom region is supplied
+  Note: The bbox coordinates are in the form used by 'camera ortho world ...':
+  x,y - world coord. of lower left corner, w,h - width height in world coords
+  This form is currently used only if the camera mode is set to 'image'.
+nv>legend <colormapName> <title> <rmin> <rmax> <nbytes>
+  <binary RGB data>
+nv>dataset names <Tcl list of names>
+nv>dataset scalar world <x> <y> <z> <value> <dataSetName>
+nv>dataset scalar pixel <x> <y> <value> <dataSetName>
+nv>dataset vector world <x> <y> <z> <valueX> <valueY> <valueZ> <dataSetName>
+nv>dataset vector pixel <x> <y> <valueX> <valueY> <valueZ> <dataSetName>
+nv>ok -token <seqnum>
+   Reply indicated commands through <seqnum> (numbered beginning at 1) have been
+   processed, but no new image was rendered
+================================================================================
+Error Replies:
+================================================================================
+nv>viserror -bytes <nbytes>
+   <multi-line error string of nbytes>
+================================================================================
Index: vtkvis/tags/1.8.4/vtkRpAxisActor.cpp
===================================================================
--- vtkvis/tags/1.8.4/vtkRpAxisActor.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/vtkRpAxisActor.cpp	(revision 6184)
@@ -0,0 +1,2141 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkRpAxisActor.cxx
+  Thanks:    Kathleen Bonnell, B Division, Lawrence Livermore Nat'l Laboratory
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+=========================================================================*/
+#include "vtkRpAxisActor.h"
+
+#include "vtkRpAxisFollower.h"
+#include "vtkCamera.h"
+#include "vtkCellArray.h"
+#include "vtkCoordinate.h"
+#include "vtkFollower.h"
+#include "vtkTextRenderer.h"
+#include "vtkMath.h"
+#include "vtkObjectFactory.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "vtkProperty2D.h"
+#include "vtkStringArray.h"
+#include "vtkTextActor.h"
+#include "vtkTextProperty.h"
+#include "vtkVectorText.h"
+#include "vtkViewport.h"
+#include "vtkVersion.h"
+#include "vtkWindow.h"
+
+vtkStandardNewMacro(vtkRpAxisActor);
+vtkCxxSetObjectMacro(vtkRpAxisActor, Camera, vtkCamera);
+vtkCxxSetObjectMacro(vtkRpAxisActor,LabelTextProperty,vtkTextProperty);
+vtkCxxSetObjectMacro(vtkRpAxisActor,TitleTextProperty,vtkTextProperty);
+
+// ****************************************************************
+// Instantiate this object.
+// ****************************************************************
+
+vtkRpAxisActor::vtkRpAxisActor()
+{
+  this->Point1Coordinate = vtkCoordinate::New();
+  this->Point1Coordinate->SetCoordinateSystemToWorld();
+  this->Point1Coordinate->SetValue(0.0, 0.0, 0.0);
+
+  this->Point2Coordinate = vtkCoordinate::New();
+  this->Point2Coordinate->SetCoordinateSystemToWorld();
+  this->Point2Coordinate->SetValue(0.75, 0.0, 0.0);
+
+  this->Camera = NULL;
+  this->Title = NULL;
+  this->MinorTicksVisible = 1;
+  this->MajorTickSize = 1.0;
+  this->MinorTickSize = 0.5;
+  this->TickLocation = VTK_TICKS_INSIDE;
+  this->Range[0] = 0.0;
+  this->Range[1] = 1.0;
+  this->ScreenSize = 10.;
+  this->LabelOffset = 20.;
+  this->TitleOffset = 20.;
+
+  this->Bounds[0] = this->Bounds[2] = this->Bounds[4] = -1;
+  this->Bounds[1] = this->Bounds[3] = this->Bounds[5] = 1;
+
+  this->LabelFormat = new char[8];
+  sprintf(this->LabelFormat, "%s", "%-#6.3g");
+
+  this->TitleTextProperty = vtkTextProperty::New();
+  this->TitleTextProperty->SetColor(0.,0.,0.);
+  this->TitleTextProperty->SetFontFamilyToArial();
+  this->TitleTextProperty->SetFontSize(18);
+  this->TitleTextProperty->SetVerticalJustificationToCentered();
+  this->TitleTextProperty->SetJustificationToCentered();
+
+  this->TitleVector = vtkVectorText::New();
+  this->TitleMapper = vtkPolyDataMapper::New();
+  this->TitleMapper->SetInputConnection(
+    this->TitleVector->GetOutputPort());
+  this->TitleActor = vtkRpAxisFollower::New();
+  this->TitleActor->SetAxis(this);
+  this->TitleActor->SetMapper(this->TitleMapper);
+  this->TitleActor->SetEnableDistanceLOD(0);
+  this->TitleActor2D = vtkTextActor::New();
+
+  this->NumberOfLabelsBuilt = 0;
+  this->LabelVectors = NULL;
+  this->LabelMappers = NULL;
+  this->LabelActors = NULL;
+  this->LabelActors2D = NULL;
+
+  this->LabelTextProperty = vtkTextProperty::New();
+  this->LabelTextProperty->SetColor(0.,0.,0.);
+  this->LabelTextProperty->SetFontFamilyToArial();
+  this->LabelTextProperty->SetFontSize(14);
+  this->LabelTextProperty->SetVerticalJustificationToBottom();
+  this->LabelTextProperty->SetJustificationToLeft();
+
+  this->AxisLines = vtkPolyData::New();
+  this->AxisLinesMapper = vtkPolyDataMapper::New();
+  this->AxisLinesMapper->SetInputData(this->AxisLines);
+  this->AxisLinesActor = vtkActor::New();
+  this->AxisLinesActor->SetMapper(this->AxisLinesMapper);
+  this->Gridlines = vtkPolyData::New();
+  this->GridlinesMapper = vtkPolyDataMapper::New();
+  this->GridlinesMapper->SetInputData(this->Gridlines);
+  this->GridlinesActor = vtkActor::New();
+  this->GridlinesActor->SetMapper(this->GridlinesMapper);
+  this->InnerGridlines = vtkPolyData::New();
+  this->InnerGridlinesMapper = vtkPolyDataMapper::New();
+  this->InnerGridlinesMapper->SetInputData(this->InnerGridlines);
+  this->InnerGridlinesActor = vtkActor::New();
+  this->InnerGridlinesActor->SetMapper(this->InnerGridlinesMapper);
+  this->Gridpolys = vtkPolyData::New();
+  this->GridpolysMapper = vtkPolyDataMapper::New();
+  this->GridpolysMapper->SetInputData(this->Gridpolys);
+  this->GridpolysActor = vtkActor::New();
+  this->GridpolysActor->SetMapper(this->GridpolysMapper);
+
+  this->AxisVisibility = 1;
+  this->TickVisibility = 1;
+  this->LabelVisibility = 1;
+  this->TitleVisibility = 1;
+
+  this->DrawGridlines = 0;
+  this->DrawGridlinesOnly = 0;
+  this->GridlineXLength = 1.;
+  this->GridlineYLength = 1.;
+  this->GridlineZLength = 1.;
+
+  this->DrawInnerGridlines = 0;
+
+  this->DrawGridpolys = 0;
+
+  this->AxisType = VTK_AXIS_TYPE_X;
+  //
+  // AxisPosition denotes which of the four possibilities in relation
+  // to the bounding box.  An x-Type axis with min min, means the x-axis
+  // at minimum y and minimum z values of the bbox.
+  //
+  this->AxisPosition = VTK_AXIS_POS_MINMIN;
+
+  this->LastLabelStart = 100000;
+
+  this->LastAxisPosition = -1;
+  this->LastTickLocation = -1;
+  this->LastTickVisibility = -1;
+  this->LastDrawGridlines = -1;
+  this->LastDrawInnerGridlines = -1;
+  this->LastDrawGridpolys = -1;
+  this->LastMinorTicksVisible = -1;
+  this->LastRange[0] = -1.0;
+  this->LastRange[1] = -1.0;
+
+  this->MinorTickPts = vtkPoints::New();
+  this->MajorTickPts = vtkPoints::New();
+  this->GridlinePts  = vtkPoints::New();
+  this->InnerGridlinePts  = vtkPoints::New();
+  this->GridpolyPts  = vtkPoints::New();
+
+  this->AxisHasZeroLength = false;
+
+  this->MinorStart = 0.;
+  //this->MajorStart = 0.;
+  for(int i=0;i<3;i++)
+    {
+    this->MajorStart[i] = 0.;
+    }
+  this->DeltaMinor = 1.;
+  //this->DeltaMajor = 1.;
+  for(int i=0;i<3;i++)
+    {
+    this->DeltaMajor[i] = 1.;
+    }
+
+  this->MinorRangeStart = 0.;
+  this->MajorRangeStart = 0.;
+  this->DeltaRangeMinor = 1.;
+  this->DeltaRangeMajor = 1.;
+
+  this->CalculateTitleOffset = 1;
+  this->CalculateLabelOffset = 1;
+
+  // Instance variables specific to 2D mode
+  this->Use2DMode = 0;
+  this->SaveTitlePosition = 0;
+  this->TitleConstantPosition[0] = this->TitleConstantPosition[1] = 0.;
+  this->VerticalOffsetXTitle2D = -40.;
+  this->HorizontalOffsetYTitle2D = -50.;
+  this->LastMinDisplayCoordinate[0] = 0;
+  this->LastMinDisplayCoordinate[1] = 0;
+  this->LastMinDisplayCoordinate[2] = 0;
+  this->LastMaxDisplayCoordinate[0] = 0;
+  this->LastMaxDisplayCoordinate[1] = 0;
+  this->LastMaxDisplayCoordinate[2] = 0;
+
+   // 0: All locations
+  this->DrawGridlinesLocation = this->LastDrawGridlinesLocation = 0;
+
+  // reset the base
+  for(int i=0;i<3;i++)
+    {
+    this->AxisBaseForX[i] = this->AxisBaseForY[i] = this->AxisBaseForZ[i] = 0.0;
+    }
+  this->AxisBaseForX[0] = this->AxisBaseForY[1] = this->AxisBaseForZ[2] = 1.0;
+  this->AxisOnOrigin = 0;
+}
+
+// ****************************************************************
+vtkRpAxisActor::~vtkRpAxisActor()
+{
+  this->SetCamera(NULL);
+
+  if (this->Point1Coordinate)
+    {
+    this->Point1Coordinate->Delete();
+    this->Point1Coordinate = NULL;
+    }
+
+  if (this->Point2Coordinate)
+    {
+    this->Point2Coordinate->Delete();
+    this->Point2Coordinate = NULL;
+    }
+
+  delete [] this->LabelFormat;
+  this->LabelFormat = NULL;
+
+  if (this->TitleVector)
+    {
+    this->TitleVector->Delete();
+    this->TitleVector = NULL;
+    }
+  if (this->TitleMapper)
+    {
+    this->TitleMapper->Delete();
+    this->TitleMapper = NULL;
+    }
+  if (this->TitleActor)
+    {
+    this->TitleActor->Delete();
+    this->TitleActor = NULL;
+    }
+  if (this->TitleActor2D)
+    {
+    this->TitleActor2D->Delete();
+    this->TitleActor2D = NULL;
+    }
+
+  delete [] this->Title;
+  this->Title = NULL;
+
+  if (this->TitleTextProperty)
+    {
+    this->TitleTextProperty->Delete();
+    this->TitleTextProperty = NULL;
+    }
+
+  if (this->LabelMappers != NULL)
+    {
+    for (int i=0; i < this->NumberOfLabelsBuilt; i++)
+      {
+      this->LabelVectors[i]->Delete();
+      this->LabelMappers[i]->Delete();
+      this->LabelActors[i]->Delete();
+      this->LabelActors2D[i]->Delete();
+      }
+    this->NumberOfLabelsBuilt = 0;
+    delete [] this->LabelVectors;
+    delete [] this->LabelMappers;
+    delete [] this->LabelActors;
+    delete [] this->LabelActors2D;
+    this->LabelVectors = NULL;
+    this->LabelMappers = NULL;
+    this->LabelActors = NULL;
+    this->LabelActors2D = NULL;
+    }
+  if (this->LabelTextProperty)
+    {
+    this->LabelTextProperty->Delete();
+    this->LabelTextProperty = NULL;
+    }
+
+  if (this->AxisLines)
+    {
+    this->AxisLines->Delete();
+    this->AxisLines = NULL;
+    }
+  if (this->AxisLinesMapper)
+    {
+    this->AxisLinesMapper->Delete();
+    this->AxisLinesMapper = NULL;
+    }
+  if (this->AxisLinesActor)
+    {
+    this->AxisLinesActor->Delete();
+    this->AxisLinesActor = NULL;
+    }
+
+  if (this->Gridlines)
+    {
+    this->Gridlines->Delete();
+    this->Gridlines = NULL;
+    }
+  if (this->GridlinesMapper)
+    {
+    this->GridlinesMapper->Delete();
+    this->GridlinesMapper = NULL;
+    }
+  if (this->GridlinesActor)
+    {
+    this->GridlinesActor->Delete();
+    this->GridlinesActor = NULL;
+    }
+
+  if (this->InnerGridlines)
+    {
+    this->InnerGridlines->Delete();
+    this->InnerGridlines = NULL;
+    }
+  if (this->InnerGridlinesMapper)
+    {
+    this->InnerGridlinesMapper->Delete();
+    this->InnerGridlinesMapper = NULL;
+    }
+  if (this->InnerGridlinesActor)
+    {
+    this->InnerGridlinesActor->Delete();
+    this->InnerGridlinesActor = NULL;
+    }
+
+  if (this->Gridpolys)
+    {
+    this->Gridpolys->Delete();
+    this->Gridpolys = NULL;
+    }
+  if (this->GridpolysMapper)
+    {
+    this->GridpolysMapper->Delete();
+    this->GridpolysMapper = NULL;
+    }
+  if (this->GridpolysActor)
+    {
+    this->GridpolysActor->Delete();
+    this->GridpolysActor = NULL;
+    }
+
+  if (this->MinorTickPts)
+    {
+    this->MinorTickPts ->Delete();
+    this->MinorTickPts = NULL;
+    }
+  if (this->MajorTickPts)
+    {
+    this->MajorTickPts->Delete();
+    this->MajorTickPts = NULL;
+    }
+  if (this->GridlinePts)
+    {
+    this->GridlinePts->Delete();
+    this->GridlinePts = NULL;
+    }
+  if (this->InnerGridlinePts)
+    {
+    this->InnerGridlinePts->Delete();
+    this->InnerGridlinePts = NULL;
+    }
+  if (this->GridpolyPts)
+    {
+    this->GridpolyPts->Delete();
+    this->GridpolyPts = NULL;
+    }
+}
+
+// ****************************************************************
+void vtkRpAxisActor::ReleaseGraphicsResources(vtkWindow *win)
+{
+  this->TitleActor->ReleaseGraphicsResources(win);
+  this->TitleActor2D->ReleaseGraphicsResources(win);
+  for (int i=0; i < this->NumberOfLabelsBuilt; i++)
+    {
+    this->LabelActors[i]->ReleaseGraphicsResources(win);
+    this->LabelActors2D[i]->ReleaseGraphicsResources(win);
+    }
+  this->AxisLinesActor->ReleaseGraphicsResources(win);
+  this->GridlinesActor->ReleaseGraphicsResources(win);
+  this->InnerGridlinesActor->ReleaseGraphicsResources(win);
+  this->GridpolysActor->ReleaseGraphicsResources(win);
+}
+
+// ****************************************************************
+int vtkRpAxisActor::RenderOpaqueGeometry(vtkViewport *viewport)
+{
+  int i, renderedSomething=0;
+
+  this->BuildAxis(viewport, false);
+
+  // Everything is built, just have to render
+
+  if (!this->AxisHasZeroLength)
+    {
+    if(this->DrawGridlinesOnly && this->DrawGridlines)
+      {
+      // Exit !!!!
+      return this->GridlinesActor->RenderOpaqueGeometry(viewport);
+      }
+    if (this->Title != NULL && this->Title[0] != 0 && this->TitleVisibility)
+      {
+      if (this->Use2DMode)
+        {
+        renderedSomething += this->TitleActor2D->RenderOpaqueGeometry(viewport);
+        }
+      else
+        {
+        renderedSomething += this->TitleActor->RenderOpaqueGeometry(viewport);
+        }
+      }
+    if (this->AxisVisibility || this->TickVisibility)
+      {
+      renderedSomething += this->AxisLinesActor->RenderOpaqueGeometry(viewport);
+      }
+    if(this->DrawGridlines)
+      {
+      renderedSomething += this->GridlinesActor->RenderOpaqueGeometry(viewport);
+      }
+    if(this->DrawInnerGridlines)
+      {
+      renderedSomething += this->InnerGridlinesActor->RenderOpaqueGeometry(viewport);
+      }
+    if (this->LabelVisibility)
+      {
+      for (i=0; i<this->NumberOfLabelsBuilt; i++)
+        {
+        if (this->Use2DMode)
+          {
+          renderedSomething +=
+            this->LabelActors2D[i]->RenderOpaqueGeometry(viewport);
+          }
+        else
+          {
+          renderedSomething +=
+            this->LabelActors[i]->RenderOpaqueGeometry(viewport);
+          }
+        }
+      }
+    }
+
+  return renderedSomething;
+}
+
+// ****************************************************************
+// Build the translucent poly actors and render.
+// ****************************************************************
+int vtkRpAxisActor::RenderTranslucentGeometry(vtkViewport *viewport)
+{
+  return this->RenderTranslucentPolygonalGeometry(viewport);
+}
+
+// ****************************************************************
+// Build the translucent poly actors and render.
+// ****************************************************************
+int vtkRpAxisActor::RenderTranslucentPolygonalGeometry(vtkViewport *viewport)
+{
+
+  int renderedSomething=0;
+
+  this->BuildAxis(viewport, false);
+
+  // Everything is built, just have to render
+
+  if (!this->AxisHasZeroLength && !this->DrawGridlinesOnly)
+    {
+    if(this->DrawGridpolys)
+      {
+      renderedSomething += this->GridpolysActor->RenderTranslucentPolygonalGeometry(viewport);
+      }
+    if (this->Title != NULL && this->Title[0] != 0 && this->TitleVisibility)
+      {
+      if (this->Use2DMode)
+        {
+        renderedSomething += this->TitleActor2D->RenderTranslucentPolygonalGeometry(viewport);
+        }
+      else
+        {
+        renderedSomething += this->TitleActor->RenderTranslucentPolygonalGeometry(viewport);
+        }
+      }
+    if (this->LabelVisibility)
+      {
+      for (int i=0; i<this->NumberOfLabelsBuilt; i++)
+        {
+        if (this->Use2DMode)
+          {
+          renderedSomething +=
+            this->LabelActors2D[i]->RenderTranslucentPolygonalGeometry(viewport);
+          }
+        else
+          {
+          renderedSomething +=
+            this->LabelActors[i]->RenderTranslucentPolygonalGeometry(viewport);
+          }
+        }
+      }
+    }
+  return renderedSomething;
+}
+
+// ****************************************************************
+// Render the 2d annotations.
+// ****************************************************************
+int vtkRpAxisActor::RenderOverlay(vtkViewport *viewport)
+{
+  int i, renderedSomething=0;
+
+  // Everything is built, just have to render
+  if (!this->AxisHasZeroLength && !this->DrawGridlinesOnly)
+    {
+    if (this->Use2DMode)
+      {
+      renderedSomething += this->TitleActor2D->RenderOverlay(viewport);
+      }
+    else
+      {
+      renderedSomething += this->TitleActor->RenderOverlay(viewport);
+      }
+    if (this->LabelVisibility)
+      {
+      for (i=0; i<this->NumberOfLabelsBuilt; i++)
+        {
+        if (this->Use2DMode)
+          {
+          renderedSomething += this->LabelActors2D[i]->RenderOverlay(viewport);
+          }
+        else
+          {
+          renderedSomething += this->LabelActors[i]->RenderOverlay(viewport);
+          }
+        }
+      }
+    }
+
+  return renderedSomething;
+}
+
+// **************************************************************************
+int vtkRpAxisActor::HasTranslucentPolygonalGeometry()
+{
+  if (this->Visibility && !this->AxisHasZeroLength)
+    {
+
+    if (this->TitleVisibility)
+      {
+      if (this->Use2DMode)
+        {
+        if (this->TitleActor2D->HasTranslucentPolygonalGeometry())
+          {
+          return 1;
+          }
+        }
+      else
+        {
+        if (this->TitleActor->HasTranslucentPolygonalGeometry())
+          {
+          return 1;
+          }
+        }
+      }
+
+    if (this->LabelVisibility)
+      {
+      if (this->Use2DMode)
+        {
+        for (int i = 0; i < this->NumberOfLabelsBuilt; ++i)
+          {
+          if (this->LabelActors2D[i]->HasTranslucentPolygonalGeometry())
+            {
+            return 1;
+            } // end if
+          } // end for
+        } // end 2D
+      else
+        {
+        for (int i = 0; i < this->NumberOfLabelsBuilt; ++i)
+          {
+          if (this->LabelActors[i]->HasTranslucentPolygonalGeometry())
+            {
+            return 1;
+            } // end if
+          } // end for
+        } // end 3D
+      } // end label vis
+
+    if (this->AxisLinesActor->HasTranslucentPolygonalGeometry())
+      {
+      return 1;
+      }
+
+    if (this->DrawGridlines &&
+        this->GridlinesActor->HasTranslucentPolygonalGeometry())
+      {
+      return 1;
+      }
+
+    if (this->DrawInnerGridlines &&
+        this->InnerGridlinesActor->HasTranslucentPolygonalGeometry())
+      {
+      return 1;
+      }
+
+    if (this->DrawGridpolys &&
+        this->GridpolysActor->HasTranslucentPolygonalGeometry())
+      {
+      return 1;
+      }
+
+    return this->Superclass::HasTranslucentPolygonalGeometry();
+
+    } // end this vis
+
+  return 0;
+}
+
+// **************************************************************************
+// Perform some initialization, determine which Axis type we are
+// **************************************************************************
+void vtkRpAxisActor::BuildAxis(vtkViewport *viewport, bool force)
+{
+  // We'll do our computation in world coordinates. First determine the
+  // location of the endpoints.
+  double *x, p1[3], p2[3];
+  x = this->Point1Coordinate->GetValue();
+  p1[0] = x[0]; p1[1] = x[1]; p1[2] = x[2];
+  x = this->Point2Coordinate->GetValue();
+  p2[0] = x[0]; p2[1] = x[1]; p2[2] = x[2];
+
+  //
+  //  Test for axis of zero length.
+  //
+  if (p1[0] == p2[0] && p1[1] == p2[1] && p1[2] == p2[2])
+    {
+    vtkDebugMacro(<<"Axis has zero length, not building.");
+    this->AxisHasZeroLength = true;
+    return;
+    }
+  this->AxisHasZeroLength = false;
+
+  if (!force && this->GetMTime() < this->BuildTime.GetMTime() &&
+      viewport->GetMTime() < this->BuildTime.GetMTime())
+    {
+    return; //already built
+    }
+
+  vtkDebugMacro(<<"Rebuilding axis");
+
+  if (force || this->GetProperty()->GetMTime() > this->BuildTime.GetMTime())
+    {
+      //this->AxisLinesActor->SetProperty(this->GetProperty());
+    this->TitleActor->SetProperty(this->GetProperty());
+    this->TitleActor->GetProperty()->SetColor(this->TitleTextProperty->GetColor());
+    this->TitleActor->GetProperty()->SetOpacity(this->TitleTextProperty->GetOpacity());
+    }
+
+  //
+  // Generate the axis and tick marks.
+  //
+  bool ticksRebuilt;
+  ticksRebuilt = this->BuildTickPoints(p1, p2, force);
+
+  bool tickVisChanged = this->TickVisibilityChanged();
+
+  if (force || ticksRebuilt || tickVisChanged || this->LastDrawGridlinesLocation != this->DrawGridlinesLocation)
+    {
+    this->LastDrawGridlinesLocation = this->DrawGridlinesLocation;
+    this->SetAxisPointsAndLines();
+    }
+
+  // If the ticks have been rebuilt it is more than likely
+  // that the labels should follow...
+  this->BuildLabels(viewport, force || ticksRebuilt);
+  if (this->Use2DMode == 1)
+    {
+    this->BuildLabels2D(viewport, force || ticksRebuilt);
+    }
+
+  if (this->Title != NULL && this->Title[0] != 0)
+    {
+    this->BuildTitle(force || ticksRebuilt);
+    if( this->Use2DMode == 1 )
+      {
+      this->BuildTitle2D(viewport, force || ticksRebuilt);
+      }
+    }
+
+  this->LastAxisPosition = this->AxisPosition;
+
+  this->LastRange[0] = this->Range[0];
+  this->LastRange[1] = this->Range[1];
+  this->BuildTime.Modified();
+}
+
+// ****************************************************************
+//  Set label values and properties.
+// ****************************************************************
+void
+vtkRpAxisActor::BuildLabels(vtkViewport *viewport, bool force)
+{
+  if (!force && !this->LabelVisibility)
+    {
+    return;
+    }
+
+  for (int i = 0; i < this->NumberOfLabelsBuilt; i++)
+    {
+    this->LabelActors[i]->SetCamera(this->Camera);
+    this->LabelActors[i]->SetProperty(this->GetProperty());
+    this->LabelActors[i]->GetProperty()->SetColor(this->LabelTextProperty->GetColor());
+    this->LabelActors[i]->GetProperty()->SetOpacity(this->LabelTextProperty->GetOpacity());
+    this->LabelActors[i]->SetOrientation(0., 0., this->LabelTextProperty->GetOrientation());
+
+    if(!this->GetCalculateLabelOffset())
+      {
+      this->LabelActors[i]->SetAutoCenter(1);
+      }
+    }
+
+  if (force || this->BuildTime.GetMTime() <  this->BoundsTime.GetMTime() ||
+      this->AxisPosition != this->LastAxisPosition ||
+      this->LastRange[0] != this->Range[0] ||
+      this->LastRange[1] != this->Range[1])
+    {
+    this->SetLabelPositions(viewport, force);
+    }
+}
+
+static const int vtkRpAxisActorMultiplierTable1[4] = { -1, -1, 1,  1};
+static const int vtkRpAxisActorMultiplierTable2[4] = { -1,  1, 1, -1};
+
+// *******************************************************************
+// Determine and set scale factor and position for labels.
+// *******************************************************************
+void vtkRpAxisActor::SetLabelPositions(vtkViewport *viewport, bool force)
+{
+  if (!force && (!this->LabelVisibility || this->NumberOfLabelsBuilt == 0))
+    {
+    return;
+    }
+
+  double bounds[6], center[3], tick[3], pos[3];
+  int i = 0;
+  int xmult = 0;
+  int ymult = 0;
+  double labelAngle = vtkMath::RadiansFromDegrees(this->LabelTextProperty->GetOrientation());
+  double labelCos = fabs(cos(labelAngle));
+  double labelSin = fabs(sin(labelAngle));
+
+
+  switch (this->AxisType)
+    {
+    case VTK_AXIS_TYPE_X :
+      xmult = 0;
+      ymult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      break;
+    case VTK_AXIS_TYPE_Y :
+      xmult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      ymult = 0;
+      break;
+    case VTK_AXIS_TYPE_Z :
+      xmult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      ymult = vtkRpAxisActorMultiplierTable2[this->AxisPosition];
+      break;
+    }
+
+  int ptIdx;
+  //
+  // xadjust & yadjust are used for positioning the label correctly
+  // depending upon the 'orientation' of the axis as determined
+  // by its position in view space (via transformed bounds).
+  //
+  double displayBounds[6] = { 0., 0., 0., 0., 0., 0.};
+  this->TransformBounds(viewport, displayBounds);
+  double xadjust = (displayBounds[0] > displayBounds[1] ? -1 : 1);
+  double yadjust = (displayBounds[2] > displayBounds[3] ? -1 : 1);
+
+  for (i=0; i < this->NumberOfLabelsBuilt &&
+    i < this->MajorTickPts->GetNumberOfPoints(); i++)
+    {
+    ptIdx = 4*i + 1;
+    this->MajorTickPts->GetPoint(ptIdx, tick);
+
+    this->LabelActors[i]->GetMapper()->GetBounds(bounds);
+
+    if(this->CalculateLabelOffset)
+      {
+      double halfWidth  = 0.5 * ((bounds[1] - bounds[0]) * labelCos + (bounds[3] - bounds[2]) * labelSin);
+      double halfHeight = 0.5 * ((bounds[1] - bounds[0]) * labelSin + (bounds[3] - bounds[2]) * labelCos);
+
+      center[0] = tick[0] + xmult * (halfWidth  + this->MinorTickSize);
+      center[1] = tick[1] + ymult * (halfHeight + this->MinorTickSize);
+      center[2] = tick[2];
+
+      pos[0] = (center[0] - xadjust *halfWidth);
+      pos[1] = (center[1] - yadjust *halfHeight);
+      pos[2] =  center[2];
+      }
+    else
+      {
+      pos[0] = tick[0];
+      pos[1] = tick[1];
+      pos[2] = tick[2];
+
+      double delta  = 0.5 * ((bounds[1] - bounds[0]) * labelSin + (bounds[3] - bounds[2]) * labelCos);
+      this->LabelActors[i]->SetScreenOffset(this->LabelOffset + (delta) * this->ScreenSize);
+      }
+
+    this->LabelActors[i]->SetPosition(pos[0], pos[1], pos[2]);
+    }
+}
+
+// *******************************************************************
+//  Set 2D label values and properties.
+// *******************************************************************
+void
+vtkRpAxisActor::BuildLabels2D(vtkViewport *viewport, bool force)
+{
+  if (!force && (!this->LabelVisibility || this->NumberOfLabelsBuilt == 0))
+    return;
+
+  for (int i = 0; i < this->NumberOfLabelsBuilt; i++)
+    {
+    this->LabelActors2D[i]->GetProperty()->SetColor(this->LabelTextProperty->GetColor());
+    this->LabelActors2D[i]->GetProperty()->SetOpacity(this->LabelTextProperty->GetOpacity());
+    this->LabelActors2D[i]->GetTextProperty()->ShallowCopy(this->LabelTextProperty);
+    }
+
+  this->NeedBuild2D = this->BoundsDisplayCoordinateChanged(viewport);
+  if (force || this->NeedBuild2D)
+    {
+    this->SetLabelPositions2D(viewport, force);
+    }
+}
+
+
+// *******************************************************************
+// Determine and set scale factor and position for 2D labels.
+// *******************************************************************
+void
+vtkRpAxisActor::SetLabelPositions2D(vtkViewport *viewport, bool force)
+{
+  if (!force && (!this->LabelVisibility || this->NumberOfLabelsBuilt == 0) )
+    return;
+
+  int xmult = 0;
+  int ymult = 0;
+  double xcoeff = 0.;
+  double ycoeff = 0.;
+
+  // we are in 2D mode, so no Z axis
+  switch (this->AxisType)
+    {
+    case VTK_AXIS_TYPE_X :
+      xmult = 0;
+      ymult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      xcoeff = 0.5;
+      ycoeff = 1.0;
+      break;
+    case VTK_AXIS_TYPE_Y :
+      xmult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      ymult = 0;
+      xcoeff = 1.0;
+      ycoeff = 0.5;
+      break;
+    }
+
+
+  int ptIdx;
+  //
+  // xadjust & yadjust are used for positioning the label correctly
+  // depending upon the 'orientation' of the axis as determined
+  // by its position in view space (via transformed bounds).
+  //
+  double displayBounds[6] = { 0., 0., 0., 0., 0., 0.};
+  this->TransformBounds(viewport, displayBounds);
+  double xadjust = (displayBounds[0] > displayBounds[1] ? -1 : 1);
+  double yadjust = (displayBounds[2] > displayBounds[3] ? -1 : 1);
+  double transpos[3] = {0., 0., 0.};
+  double center[3], tick[3], pos[2];
+
+  vtkTextRenderer *tren = vtkTextRenderer::GetInstance();
+  if (!tren)
+    {
+    vtkErrorMacro(<< "Unable to obtain the vtkTextRenderer instance!");
+    return;
+    }
+#if VTK_MAJOR_VERSION == 6 && VTK_MINOR_VERSION >= 3
+  vtkWindow *win = viewport->GetVTKWindow();
+  if (!win)
+    {
+    vtkErrorMacro(<<"No render window available: cannot determine DPI.");
+    return;
+    }
+
+#endif
+  for (int i = 0; i < this->NumberOfLabelsBuilt; i++)
+    {
+    ptIdx = 4*i + 1;
+
+    this->MajorTickPts->GetPoint(ptIdx, tick);
+
+    center[0] = tick[0] + xmult * this->MinorTickSize;
+    center[1] = tick[1] + ymult * this->MinorTickSize;
+    center[2] = tick[2];
+
+    viewport->SetWorldPoint(center[0], center[1], center[2], 1.0);
+    viewport->WorldToDisplay();
+    viewport->GetDisplayPoint(transpos);
+
+    int bbox[4];
+    if (!tren->GetBoundingBox(this->LabelActors2D[i]->GetTextProperty(),
+#if VTK_MAJOR_VERSION == 6 && VTK_MINOR_VERSION >= 3
+                              this->LabelActors2D[i]->GetInput(), bbox,
+                              win->GetDPI()))
+#else
+                              this->LabelActors2D[i]->GetInput(), bbox))
+#endif
+      {
+      vtkErrorMacro(<< "Unable to calculate bounding box for label "
+                    << this->LabelActors2D[i]->GetInput());
+      continue;
+      }
+
+    double width  = (bbox[1]-bbox[0]);
+    double height = (bbox[3]-bbox[2]);
+
+    pos[0] = (transpos[0] - xadjust*width*xcoeff);
+    pos[1] = (transpos[1] - yadjust*height*ycoeff);
+    this->LabelActors2D[i]->SetPosition( pos[0], pos[1] );
+    }
+}
+
+
+// **********************************************************************
+//  Determines scale and position for the Title.  Currently,
+//  title can only be centered with respect to its axis.
+// **********************************************************************
+void vtkRpAxisActor::BuildTitle(bool force)
+{
+  this->NeedBuild2D = false;
+  if (!force && !this->TitleVisibility)
+    {
+    return;
+    }
+  double labBounds[6], titleBounds[6], center[3], pos[3];
+  double labHeight, maxHeight = 0, labWidth, maxWidth = 0;
+  double halfTitleWidth, halfTitleHeight;
+  double labelAngle = vtkMath::RadiansFromDegrees(this->LabelTextProperty->GetOrientation());
+  double labelCos = fabs(cos(labelAngle));
+  double labelSin = fabs(sin(labelAngle));
+
+  double *p1 = this->Point1Coordinate->GetValue();
+  double *p2 = this->Point2Coordinate->GetValue();
+  int xmult = 0;
+  int ymult = 0;
+
+  if (!force && this->LabelBuildTime.GetMTime() < this->BuildTime.GetMTime() &&
+      this->BoundsTime.GetMTime() < this->BuildTime.GetMTime() &&
+      this->AxisPosition == this->LastAxisPosition &&
+      this->TitleTextTime.GetMTime() < this->BuildTime.GetMTime())
+    {
+    return;
+    }
+
+  this->NeedBuild2D = true;
+  switch (this->AxisType)
+    {
+    case VTK_AXIS_TYPE_X :
+      xmult = 0;
+      ymult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      break;
+    case VTK_AXIS_TYPE_Y :
+      xmult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      ymult = 0;
+      break;
+    case VTK_AXIS_TYPE_Z :
+      xmult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+      ymult = vtkRpAxisActorMultiplierTable2[this->AxisPosition];
+      break;
+    }
+  //
+  //  Title should be in relation to labels (if any)
+  //  so find out information about them.
+  //
+  for (int i = 0; i < this->NumberOfLabelsBuilt; i++)
+    {
+    this->LabelActors[i]->GetMapper()->GetBounds(labBounds);
+    labWidth = (labBounds[1] - labBounds[0]) * labelCos + (labBounds[3] - labBounds[2]) * labelSin;
+    maxWidth = (labWidth > maxWidth ? labWidth : maxWidth);
+    labHeight = (labBounds[1] - labBounds[0]) * labelSin + (labBounds[3] - labBounds[2]) * labelCos;
+    maxHeight = (labHeight > maxHeight ? labHeight : maxHeight);
+    }
+
+  this->TitleVector->SetText(this->Title);
+
+  this->TitleActor->GetProperty()->SetColor(this->TitleTextProperty->GetColor());
+  this->TitleActor->SetCamera(this->Camera);
+  this->TitleActor->GetMapper()->GetBounds(titleBounds);
+
+  if(!this->GetCalculateTitleOffset())
+    {
+    this->TitleActor->SetAutoCenter(1);
+    }
+
+  center[0] = p1[0] + (p2[0] - p1[0]) / 2.0;
+  center[1] = p1[1] + (p2[1] - p1[1]) / 2.0;
+  center[2] = p1[2] + (p2[2] - p1[2]) / 2.0;
+
+  halfTitleHeight = (titleBounds[3] - titleBounds[2]) * 0.5;
+  if(this->CalculateTitleOffset)
+    {
+    halfTitleWidth  = (titleBounds[1] - titleBounds[0]) * 0.5;
+    center[0] += xmult * (halfTitleWidth + maxWidth);
+    center[1] += ymult * (halfTitleHeight + 2*maxHeight);
+    }
+  else
+    {
+    this->TitleActor->SetScreenOffset(this->TitleOffset +
+      this->LabelOffset + this->ScreenSize * (maxHeight + halfTitleHeight));
+    }
+
+  pos[0] = center[0];
+  pos[1] = center[1];
+  pos[2] = center[2];
+
+  this->TitleActor->SetPosition(pos[0], pos[1], pos[2]);
+}
+
+// **********************************************************************
+//  Determines scale and position for the 2D Title.  Currently,
+//  title can only be centered with respect to its axis.
+// **********************************************************************
+void
+vtkRpAxisActor::BuildTitle2D(vtkViewport *viewport, bool force)
+{
+  if (!this->NeedBuild2D && !force && !this->TitleVisibility)
+    {
+    return;
+    }
+
+  // for textactor instead of follower
+  this->TitleActor2D->SetInput( this->TitleVector->GetText() );
+  this->TitleActor2D->GetProperty()->SetColor( this->TitleTextProperty->GetColor() );
+  this->TitleActor2D->GetProperty()->SetOpacity( this->TitleTextProperty->GetOpacity() );
+  this->TitleActor2D->GetTextProperty()->ShallowCopy(this->TitleTextProperty);
+
+  if (this->AxisType == VTK_AXIS_TYPE_Y)
+    {
+    if (strlen(this->TitleActor2D->GetInput()) > 2)
+      {
+      // warning : orientation have to be set on vtkTextActor and not on the vtkTextActor's vtkTextProperty
+      // otherwise there is a strange effect (first letter is not align with the others)
+      this->TitleActor2D->SetOrientation(90);
+      }
+    else
+      {
+      // if in the previous rendering, the orientation was set.
+      this->TitleActor2D->SetOrientation(0);
+      }
+    }
+
+  // stuff for 2D axis with TextActor
+  double transpos[3];
+  double* pos = this->TitleActor->GetPosition();
+  viewport->SetWorldPoint(pos[0], pos[1],  pos[2], 1.0);
+  viewport->WorldToDisplay();
+  viewport->GetDisplayPoint(transpos);
+  if (this->AxisType == VTK_AXIS_TYPE_X)
+    {
+    transpos[1] += this->VerticalOffsetXTitle2D;
+    }
+  else if (this->AxisType == VTK_AXIS_TYPE_Y)
+    {
+    transpos[0] += this->HorizontalOffsetYTitle2D;
+    }
+  if (transpos[1] < 10.) transpos[1] = 10.;
+  if (transpos[0] < 10.) transpos[0] = 10.;
+  if (this->SaveTitlePosition == 0)
+    {
+    this->TitleActor2D->SetPosition(transpos[0], transpos[1]);
+    }
+  else
+    {
+    if (this->SaveTitlePosition == 1)
+      {
+      TitleConstantPosition[0] = transpos[0];
+      TitleConstantPosition[1] = transpos[1];
+      this->SaveTitlePosition = 2;
+      }
+    this->TitleActor2D->SetPosition(TitleConstantPosition[0], TitleConstantPosition[1]);
+    }
+}
+
+//
+//  Transform the bounding box to display coordinates.  Used
+//  in determining orientation of the axis.
+//
+void vtkRpAxisActor::TransformBounds(vtkViewport *viewport, double bnds[6])
+{
+  double minPt[3], maxPt[3], transMinPt[3], transMaxPt[3];
+  minPt[0] = this->Bounds[0];
+  minPt[1] = this->Bounds[2];
+  minPt[2] = this->Bounds[4];
+  maxPt[0] = this->Bounds[1];
+  maxPt[1] = this->Bounds[3];
+  maxPt[2] = this->Bounds[5];
+
+  viewport->SetWorldPoint(minPt[0], minPt[1], minPt[2], 1.0);
+  viewport->WorldToDisplay();
+  viewport->GetDisplayPoint(transMinPt);
+  viewport->SetWorldPoint(maxPt[0], maxPt[1], maxPt[2], 1.0);
+  viewport->WorldToDisplay();
+  viewport->GetDisplayPoint(transMaxPt);
+
+  bnds[0] = transMinPt[0];
+  bnds[2] = transMinPt[1];
+  bnds[4] = transMinPt[2];
+  bnds[1] = transMaxPt[0];
+  bnds[3] = transMaxPt[1];
+  bnds[5] = transMaxPt[2];
+}
+
+inline double ffix(double value)
+{
+  int ivalue = static_cast<int>(value);
+  return static_cast<double>(ivalue);
+}
+
+inline double fsign(double value, double sign)
+{
+  value = fabs(value);
+  if (sign < 0.)
+    {
+    value *= -1.;
+    }
+  return value;
+}
+
+// ****************************************************************
+void vtkRpAxisActor::PrintSelf(ostream& os, vtkIndent indent)
+{
+  this->Superclass::PrintSelf(os,indent);
+
+  os << indent << "Title: " << (this->Title ? this->Title : "(none)") << "\n";
+  os << indent << "Number Of Labels Built: "
+     << this->NumberOfLabelsBuilt << "\n";
+  os << indent << "Range: ("
+     << this->Range[0] << ", "
+     << this->Range[1] << ")\n";
+
+  os << indent << "Label Format: " << this->LabelFormat << "\n";
+
+  os << indent << "Axis Visibility: "
+     << (this->AxisVisibility ? "On\n" : "Off\n");
+
+  os << indent << "Tick Visibility: "
+     << (this->TickVisibility ? "On\n" : "Off\n");
+
+  os << indent << "Label Visibility: "
+     << (this->LabelVisibility ? "On\n" : "Off\n");
+
+  os << indent << "Title Visibility: "
+     << (this->TitleVisibility ? "On\n" : "Off\n");
+
+  os << indent << "Point1 Coordinate: " << this->Point1Coordinate << "\n";
+  this->Point1Coordinate->PrintSelf(os, indent.GetNextIndent());
+
+  os << indent << "Point2 Coordinate: " << this->Point2Coordinate << "\n";
+  this->Point2Coordinate->PrintSelf(os, indent.GetNextIndent());
+
+  os << indent << "AxisType: ";
+  switch (this->AxisType)
+    {
+    case VTK_AXIS_TYPE_X:
+      os << "X Axis" << endl;
+      break;
+    case VTK_AXIS_TYPE_Y:
+      os << "Y Axis" << endl;
+      break;
+    case VTK_AXIS_TYPE_Z:
+      os << "Z Axis" << endl;
+      break;
+    default:
+      // shouldn't get here
+      ;
+    }
+
+  os << indent << "DeltaMajor: "
+     << this->DeltaMajor[0] << ","
+     << this->DeltaMajor[1] << ","
+     << this->DeltaMajor[2] << endl;
+  os << indent << "DeltaMinor: " << this->DeltaMinor << endl;
+  os << indent << "DeltaRangeMajor: " << this->DeltaRangeMajor << endl;
+  os << indent << "DeltaRangeMinor: " << this->DeltaRangeMinor << endl;
+  os << indent << "MajorRangeStart: " << this->MajorRangeStart << endl;
+  os << indent << "MinorRangeStart: " << this->MinorRangeStart << endl;
+
+  os << indent << "MinorTicksVisible: " << this->MinorTicksVisible << endl;
+
+  os << indent << "TitleActor: ";
+  if(this->TitleActor)
+    {
+    os << indent << "TitleActor: (" << this->TitleActor << ")\n";
+    }
+  else
+    {
+    os << "(none)" << endl;
+    }
+
+  os << indent << "Camera: ";
+  if (this->Camera)
+    {
+    this->Camera->PrintSelf(os, indent);
+    }
+  else
+    {
+    os << "(none)" << endl;
+    }
+
+  os << indent << "MajorTickSize: " << this->MajorTickSize << endl;
+  os << indent << "MinorTickSize: " << this->MinorTickSize << endl;
+
+  os << indent << "DrawGridlines: " << this->DrawGridlines << endl;
+
+  os << indent << "MajorStart: "
+     << this->MajorStart[0] << ","
+     << this->MajorStart[1] << ","
+     << this->MajorStart[2] << endl;
+
+  os << indent << "AxisPosition: " << this->AxisPosition << endl;
+
+  os << indent << "GridlineXLength: " << this->GridlineXLength << endl;
+  os << indent << "GridlineYLength: " << this->GridlineYLength << endl;
+  os << indent << "GridlineZLength: " << this->GridlineZLength << endl;
+
+  os << indent << "DrawInnerGridpolys: " << this->DrawGridpolys << endl;
+  os << indent << "DrawInnerGridlines: " << this->DrawInnerGridlines << endl;
+
+  os << indent << "TickLocation: " << this->TickLocation << endl;
+
+  os << indent << "CalculateLabelOffset: " << this->CalculateLabelOffset << std::endl;
+  os << indent << "CalculateTitleOffset: " << this->CalculateTitleOffset << std::endl;
+
+  os << indent << "LabelTextProperty: " << this->LabelTextProperty << endl;
+  os << indent << "TitleTextProperty: " << this->TitleTextProperty << endl;
+
+  os << indent << "Use2DMode: " << this->Use2DMode << endl;
+  os << indent << "SaveTitlePosition: " << this->SaveTitlePosition << endl;
+  os << indent << "VerticalOffsetXTitle2D" << this->VerticalOffsetXTitle2D << endl;
+  os << indent << "HorizontalOffsetYTitle2D" << this->HorizontalOffsetYTitle2D << endl;
+  os << indent << "LastMinDisplayCoordinates: ("
+     << this->LastMinDisplayCoordinate[0] << ", "
+     << this->LastMinDisplayCoordinate[1] << ", "
+     << this->LastMinDisplayCoordinate[2] << ")" << endl;
+  os << indent << "LastMaxDisplayCoordinates: ("
+     << this->LastMaxDisplayCoordinate[0] << ", "
+     << this->LastMaxDisplayCoordinate[1] << ", "
+     << this->LastMaxDisplayCoordinate[2] << ")" << endl;
+  }
+
+// **************************************************************************
+// Sets text string for label vectors.  Allocates memory if necessary.
+// **************************************************************************
+void vtkRpAxisActor::SetLabels(vtkStringArray *labels)
+{
+  //
+  // If the number of labels has changed, re-allocate the correct
+  // amount of memory.
+  //
+  int i, numLabels = labels->GetNumberOfValues();
+  if (numLabels < 0)
+    {
+    vtkErrorMacro(<< "Number of labels " << numLabels << " is invalid");
+    return;
+    }
+  if (this->NumberOfLabelsBuilt != numLabels)
+    {
+    if (this->LabelMappers != NULL)
+      {
+      for (i = 0; i < this->NumberOfLabelsBuilt; i++)
+        {
+        this->LabelVectors[i]->Delete();
+        this->LabelMappers[i]->Delete();
+        this->LabelActors[i]->Delete();
+        this->LabelActors2D[i]->Delete();
+        }
+      delete [] this->LabelVectors;
+      delete [] this->LabelMappers;
+      delete [] this->LabelActors;
+      delete [] this->LabelActors2D;
+      }
+
+    this->LabelVectors = new vtkVectorText * [numLabels];
+    this->LabelMappers = new vtkPolyDataMapper * [numLabels];
+    this->LabelActors  = new vtkRpAxisFollower * [numLabels];
+    this->LabelActors2D = new vtkTextActor * [numLabels];
+
+    for (i = 0; i < numLabels; i++)
+      {
+      this->LabelVectors[i] = vtkVectorText::New();
+      this->LabelMappers[i] = vtkPolyDataMapper::New();
+      this->LabelMappers[i]->SetInputConnection(
+        this->LabelVectors[i]->GetOutputPort());
+      this->LabelActors[i] = vtkRpAxisFollower::New();
+      this->LabelActors[i]->SetAxis(this);
+      this->LabelActors[i]->SetMapper(this->LabelMappers[i]);
+      this->LabelActors[i]->SetEnableDistanceLOD(0);
+      this->LabelActors[i]->GetProperty()->SetAmbient(1.);
+      this->LabelActors[i]->GetProperty()->SetDiffuse(0.);
+      this->LabelActors[i]->GetProperty()->SetColor(this->LabelTextProperty->GetColor());
+      this->LabelActors[i]->GetProperty()->SetOpacity(this->LabelTextProperty->GetOpacity());
+      this->LabelActors2D[i] = vtkTextActor::New();
+      }
+    }
+
+  //
+  // Set the label vector text.
+  //
+  for (i = 0; i < numLabels; i++)
+    {
+    this->LabelVectors[i]->SetText(labels->GetValue(i).c_str());
+    this->LabelActors2D[i]->SetInput(this->LabelVectors[i]->GetText());
+    }
+  this->NumberOfLabelsBuilt = numLabels;
+  this->LabelBuildTime.Modified();
+}
+
+// **************************************************************************
+// Creates Poly data (lines) from tickmarks (minor/major), gridlines, and axis.
+// **************************************************************************
+void vtkRpAxisActor::SetAxisPointsAndLines()
+{
+  vtkPoints *pts = vtkPoints::New();
+  vtkCellArray *lines = vtkCellArray::New();
+  vtkCellArray *gridlines = vtkCellArray::New();
+  vtkCellArray *innerGridlines = vtkCellArray::New();
+  vtkCellArray *polys = vtkCellArray::New();
+  this->AxisLines->SetPoints(pts);
+  this->AxisLines->SetLines(lines);
+  this->Gridlines->SetPoints(this->GridlinePts);
+  this->Gridlines->SetLines(gridlines);
+  this->InnerGridlines->SetPoints(this->InnerGridlinePts);
+  this->InnerGridlines->SetLines(innerGridlines);
+  this->Gridpolys->SetPoints(this->GridpolyPts);
+  this->Gridpolys->SetPolys(polys);
+  pts->Delete();
+  lines->Delete();
+  gridlines->Delete();
+  innerGridlines->Delete();
+  polys->Delete();
+  int i, numMinorTickPts, numGridlines, numInnerGridlines, numMajorTickPts, numGridpolys, numLines;
+  vtkIdType ptIds[2];
+  vtkIdType polyPtIds[4];
+
+  if (this->TickVisibility)
+    {
+    if (this->MinorTicksVisible)
+      {
+      // In 2D mode, the minorTickPts for yz portion or xz portion have been removed.
+      numMinorTickPts = this->MinorTickPts->GetNumberOfPoints();
+      for (i = 0; i < numMinorTickPts; i++)
+        {
+        pts->InsertNextPoint(this->MinorTickPts->GetPoint(i));
+        }
+      }
+    numMajorTickPts = this->MajorTickPts->GetNumberOfPoints();
+    if (this->Use2DMode == 0)
+      {
+      for (i = 0; i < numMajorTickPts; i++)
+        {
+        pts->InsertNextPoint(this->MajorTickPts->GetPoint(i));
+        }
+      }
+    else
+      {
+      // In 2D mode, we don't need the pts for the xz portion or yz portion of the major tickmarks
+      // majorTickPts not modified because all points are used for labels' positions.
+      for (i = 0; i < numMajorTickPts; i+=4)
+        {
+        pts->InsertNextPoint(this->MajorTickPts->GetPoint(i));
+        pts->InsertNextPoint(this->MajorTickPts->GetPoint(i+1));
+        }
+      }
+    }
+
+  // create lines
+  numLines = pts->GetNumberOfPoints() / 2;
+  for (i=0; i < numLines; i++)
+    {
+    ptIds[0] = 2*i;
+    ptIds[1] = 2*i + 1;
+    lines->InsertNextCell(2, ptIds);
+    }
+
+  if (this->AxisVisibility)
+    {
+    //first axis point
+    ptIds[0] = pts->InsertNextPoint(this->Point1Coordinate->GetValue());
+    //last axis point
+    ptIds[1] = pts->InsertNextPoint(this->Point2Coordinate->GetValue());
+    lines->InsertNextCell(2, ptIds);
+    }
+  // create grid lines
+  if (this->DrawGridlines && this->AxisOnOrigin == 0)
+    {
+    numGridlines = this->GridlinePts->GetNumberOfPoints()/2;
+    int start =
+        (this->DrawGridlinesLocation == 0 || this->DrawGridlinesLocation == 1)
+        ? 0 : 1;
+    int increment = (this->DrawGridlinesLocation == 0) ? 1 : 2;
+    for (i = start; i < numGridlines; i+=increment)
+      {
+      ptIds[0] = 2*i;
+      ptIds[1] = 2*i + 1;
+      gridlines->InsertNextCell(2, ptIds);
+      }
+    }
+
+  // create inner grid lines
+  if (this->DrawInnerGridlines && this->AxisOnOrigin == 0)
+    {
+    numInnerGridlines = this->InnerGridlinePts->GetNumberOfPoints()/2;
+    for (i=0; i < numInnerGridlines; i++)
+      {
+      ptIds[0] = 2*i;
+      ptIds[1] = 2*i + 1;
+      innerGridlines->InsertNextCell(2, ptIds);
+      }
+    }
+
+  // create polys (grid polys)
+  if (this->DrawGridpolys && this->AxisOnOrigin == 0)
+    {
+    numGridpolys = this->GridpolyPts->GetNumberOfPoints()/4;
+    for (i = 0; i < numGridpolys; i++)
+      {
+      polyPtIds[0] = 4*i;
+      polyPtIds[1] = 4*i + 1;
+      polyPtIds[2] = 4*i + 2;
+      polyPtIds[3] = 4*i + 3;
+      polys->InsertNextCell(4,polyPtIds);
+      }
+    }
+}
+
+// *********************************************************************
+// Returns true if any tick vis attribute has changed since last check.
+// *********************************************************************
+bool vtkRpAxisActor::TickVisibilityChanged()
+{
+  bool retVal = (this->TickVisibility != this->LastTickVisibility) ||
+                (this->DrawGridlines != this->LastDrawGridlines)   ||
+                (this->MinorTicksVisible != this->LastMinorTicksVisible);
+
+  this->LastTickVisibility = this->TickVisibility;
+  this->LastDrawGridlines = this->DrawGridlines;
+  this->LastMinorTicksVisible = this->MinorTicksVisible;
+
+  return retVal;
+}
+
+// *********************************************************************
+// Set the bounds for this actor to use.  Sets timestamp BoundsModified.
+// *********************************************************************
+void
+vtkRpAxisActor::SetBounds(const double b[6])
+{
+  if ((this->Bounds[0] != b[0]) ||
+      (this->Bounds[1] != b[1]) ||
+      (this->Bounds[2] != b[2]) ||
+      (this->Bounds[3] != b[3]) ||
+      (this->Bounds[4] != b[4]) ||
+      (this->Bounds[5] != b[5]) )
+    {
+    for (int i = 0; i < 6; i++)
+      {
+      this->Bounds[i] = b[i];
+      }
+    this->BoundsTime.Modified();
+    }
+}
+
+// *********************************************************************
+// Retrieves the bounds of this actor.
+// *********************************************************************
+void vtkRpAxisActor::
+SetBounds(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax)
+{
+  if ((this->Bounds[0] != xmin) ||
+      (this->Bounds[1] != xmax) ||
+      (this->Bounds[2] != ymin) ||
+      (this->Bounds[3] != ymax) ||
+      (this->Bounds[4] != zmin) ||
+      (this->Bounds[5] != zmax) )
+    {
+    this->Bounds[0] = xmin;
+    this->Bounds[1] = xmax;
+    this->Bounds[2] = ymin;
+    this->Bounds[3] = ymax;
+    this->Bounds[4] = zmin;
+    this->Bounds[5] = zmax;
+
+    this->BoundsTime.Modified();
+    }
+}
+
+// *********************************************************************
+// Retrieves the bounds of this actor.
+// *********************************************************************
+double* vtkRpAxisActor::GetBounds()
+{
+  return this->Bounds;
+}
+
+// *********************************************************************
+// Retrieves the bounds of this actor.
+// *********************************************************************
+
+void vtkRpAxisActor::GetBounds(double b[6])
+{
+  for (int i = 0; i < 6; i++)
+    {
+    b[i] = this->Bounds[i];
+    }
+}
+
+// *********************************************************************
+// Method:  vtkRpAxisActor::ComputeMaxLabelLength
+// *********************************************************************
+double vtkRpAxisActor::ComputeMaxLabelLength(const double vtkNotUsed(center)[3])
+{
+  double bounds[6];
+  double xsize, ysize;
+  vtkProperty *newProp = this->NewLabelProperty();
+  double maxXSize = 0;
+  double maxYSize = 0;
+  for (int i = 0; i < this->NumberOfLabelsBuilt; i++)
+    {
+    this->LabelActors[i]->SetCamera(this->Camera);
+    this->LabelActors[i]->SetProperty(newProp);
+    this->LabelActors[i]->GetMapper()->GetBounds(bounds);
+    xsize = bounds[1] - bounds[0];
+    ysize = bounds[3] - bounds[2];
+    maxXSize = (xsize > maxXSize ? xsize : maxXSize);
+    maxYSize = (ysize > maxYSize ? ysize : maxYSize);
+    }
+  newProp->Delete();
+  return sqrt(maxXSize*maxXSize + maxYSize*maxYSize);
+}
+
+// *********************************************************************
+// Method:  vtkRpAxisActor::ComputeTitleLength
+// *********************************************************************
+double vtkRpAxisActor::ComputeTitleLength(const double vtkNotUsed(center)[3])
+{
+  double bounds[6];
+  double xsize, ysize;
+  double length;
+
+  this->TitleVector->SetText(this->Title);
+  this->TitleActor->SetCamera(this->Camera);
+  vtkProperty * newProp = this->NewTitleProperty();
+  this->TitleActor->SetProperty(newProp);
+  newProp->Delete();
+  this->TitleActor->GetMapper()->GetBounds(bounds);
+  xsize = bounds[1] - bounds[0];
+  ysize = bounds[3] - bounds[2];
+  length = sqrt(xsize*xsize + ysize*ysize);
+
+  return length;
+}
+
+// *********************************************************************
+void vtkRpAxisActor::SetLabelScale(const double s)
+{
+  for (int i=0; i < this->NumberOfLabelsBuilt; i++)
+    {
+    this->SetLabelScale(i, s);
+    }
+}
+
+// *********************************************************************
+void vtkRpAxisActor::SetLabelScale(int label, const double s)
+{
+  this->LabelActors[label]->SetScale(s);
+}
+
+// *********************************************************************
+void vtkRpAxisActor::SetTitleScale(const double s)
+{
+  this->TitleActor->SetScale(s);
+}
+
+// *********************************************************************
+void vtkRpAxisActor::SetTitle(const char *t)
+{
+  if (this->Title == NULL && t == NULL)
+    {
+    return;
+    }
+  if (this->Title && (!strcmp(this->Title, t)))
+    {
+    return;
+    }
+  delete [] this->Title;
+  if (t)
+    {
+    this->Title = new char[strlen(t)+1];
+    strcpy(this->Title, t);
+    }
+  else
+    {
+    this->Title = NULL;
+    }
+  this->TitleTextTime.Modified();
+  this->Modified();
+}
+
+// ****************************************************************************
+void vtkRpAxisActor::SetAxisLinesProperty(vtkProperty *prop)
+{
+  this->AxisLinesActor->SetProperty(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpAxisActor::GetAxisLinesProperty()
+{
+  return this->AxisLinesActor->GetProperty();
+}
+
+// ****************************************************************************
+void vtkRpAxisActor::SetGridlinesProperty(vtkProperty *prop)
+{
+  this->GridlinesActor->SetProperty(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpAxisActor::GetGridlinesProperty()
+{
+  return this->GridlinesActor->GetProperty();
+}
+
+// ****************************************************************************
+void vtkRpAxisActor::SetInnerGridlinesProperty(vtkProperty *prop)
+{
+  this->InnerGridlinesActor->SetProperty(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpAxisActor::GetInnerGridlinesProperty()
+{
+  return this->InnerGridlinesActor->GetProperty();
+}
+
+// ****************************************************************************
+void vtkRpAxisActor::SetGridpolysProperty(vtkProperty *prop)
+{
+  this->GridpolysActor->SetProperty(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpAxisActor::GetGridpolysProperty()
+{
+  return this->GridpolysActor->GetProperty();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpAxisActor::NewTitleProperty()
+{
+  vtkProperty *newProp = vtkProperty::New();
+  newProp->DeepCopy(this->GetProperty());
+  newProp->SetColor(this->TitleTextProperty->GetColor());
+  // We pass the opacity in the line offset.
+  //newProp->SetOpacity(this->TitleTextProperty->GetLineOffset());
+  return newProp;
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpAxisActor::NewLabelProperty()
+{
+  vtkProperty *newProp = vtkProperty::New();
+  newProp->DeepCopy(this->GetProperty());
+  newProp->SetColor(this->LabelTextProperty->GetColor());
+  // We pass the opacity in the line offset.
+  //newProp->SetOpacity(this->LabelTextProperty->GetLineOffset());
+  return newProp;
+}
+
+
+// ****************************************************************************
+double vtkRpAxisActor::GetDeltaMajor(int axis){
+  if(axis>=0 && axis<=2)
+    {
+    return (this->DeltaMajor[axis]);
+    }
+  return 0;
+}
+
+void vtkRpAxisActor::SetDeltaMajor(int axis, double value){
+  if(axis>=0 && axis<=2)
+    {
+    this->DeltaMajor[axis] = value;
+    }
+}
+
+// ****************************************************************************
+double vtkRpAxisActor::GetMajorStart(int axis){
+  if(axis>=0 && axis<=2)
+    {
+    return (this->MajorStart[axis]);
+    }
+  return 0;
+}
+
+// ****************************************************************************
+void vtkRpAxisActor::SetMajorStart(int axis, double value){
+  if(axis>=0 && axis<=2)
+    {
+    this->MajorStart[axis] = value;
+    }
+}
+
+// ****************************************************************************
+bool vtkRpAxisActor::BoundsDisplayCoordinateChanged(vtkViewport *viewport)
+{
+  double transMinPt[3], transMaxPt[3];
+  viewport->SetWorldPoint(this->Bounds[0], this->Bounds[2], this->Bounds[4], 1.0);
+  viewport->WorldToDisplay();
+  viewport->GetDisplayPoint(transMinPt);
+  viewport->SetWorldPoint(this->Bounds[1], this->Bounds[3], this->Bounds[5], 1.0);
+  viewport->WorldToDisplay();
+  viewport->GetDisplayPoint(transMaxPt);
+
+  if( this->LastMinDisplayCoordinate[0] != transMinPt[0]
+      || this->LastMinDisplayCoordinate[1] != transMinPt[1]
+      || this->LastMinDisplayCoordinate[2] != transMinPt[2]
+      || this->LastMaxDisplayCoordinate[0] != transMaxPt[0]
+      || this->LastMaxDisplayCoordinate[1] != transMaxPt[1]
+      || this->LastMaxDisplayCoordinate[2] != transMaxPt[2] )
+    {
+    int i = 0;
+    for( i=0 ; i<3 ; ++i )
+      {
+      this->LastMinDisplayCoordinate[i] = transMinPt[i];
+      this->LastMaxDisplayCoordinate[i] = transMaxPt[i];
+      }
+    return true;
+    }
+
+  return false;
+}
+//---------------------------------------------------------------------------
+// endpoint-related methods
+vtkCoordinate* vtkRpAxisActor::GetPoint1Coordinate()
+{
+  vtkDebugMacro(<< this->GetClassName() << " (" << this
+                << "): returning Point1 Coordinate address "
+                << this->Point1Coordinate );
+  return this->Point1Coordinate;
+}
+
+//---------------------------------------------------------------------------
+vtkCoordinate* vtkRpAxisActor::GetPoint2Coordinate()
+{
+  vtkDebugMacro(<< this->GetClassName() << " (" << this
+                << "): returning Point2 Coordinate address "
+                << this->Point2Coordinate );
+  return this->Point2Coordinate;
+}
+
+//---------------------------------------------------------------------------
+void vtkRpAxisActor::SetPoint1(double x, double y, double z)
+{
+  this->Point1Coordinate->SetValue(x, y, z);
+}
+
+//---------------------------------------------------------------------------
+void vtkRpAxisActor::SetPoint2(double x, double y, double z)
+{
+  this->Point2Coordinate->SetValue(x, y, z);
+}
+
+//---------------------------------------------------------------------------
+double* vtkRpAxisActor::GetPoint1()
+{
+  return this->Point1Coordinate->GetValue();
+}
+
+//---------------------------------------------------------------------------
+double* vtkRpAxisActor::GetPoint2()
+{
+  return this->Point2Coordinate->GetValue();
+}
+// **************************************************************************
+// Creates points for ticks (minor, major, gridlines) in correct position
+// for a generic axis.
+// **************************************************************************
+bool vtkRpAxisActor::BuildTickPoints(double p1[3], double p2[3], bool force)
+{
+  // Prevent any unwanted computation
+  if (!force && (this->AxisPosition == this->LastAxisPosition) &&
+      (this->TickLocation == this->LastTickLocation ) &&
+      (this->BoundsTime.GetMTime() < this->BuildTime.GetMTime()) &&
+      (this->Point1Coordinate->GetMTime() < this->BuildTickPointsTime.GetMTime()) &&
+      (this->Point2Coordinate->GetMTime() < this->BuildTickPointsTime.GetMTime()) &&
+      (this->Range[0] == this->LastRange[0]) &&
+      (this->Range[1] == this->LastRange[1]))
+    {
+    return false;
+    }
+
+  // Local tmp vars
+  double uPointInside[3], vPointInside[3], uPointOutside[3], vPointOutside[3];
+  double gridPointClosest[3], gridPointFarest[3], gridPointU[3], gridPointV[3];
+  double innerGridPointClosestU[3], innerGridPointClosestV[3];
+  double innerGridPointFarestU[3], innerGridPointFarestV[3];
+  double deltaVector[3];
+  double axisLength, axisShift, rangeScale, nbIterationAsDouble;
+  int nbTicks, i, nbIteration, uIndex, vIndex;
+  uIndex = vIndex = 0;
+  bool hasOrthogonalVectorBase =
+      (this->AxisBaseForX[0] == 1 && this->AxisBaseForX[1] == 0 && this->AxisBaseForX[2] == 0
+       && this->AxisBaseForY[0] == 0 && this->AxisBaseForY[1] == 1 && this->AxisBaseForY[2] == 0
+       && this->AxisBaseForZ[0] == 0 && this->AxisBaseForZ[1] == 0 && this->AxisBaseForZ[2] == 1);
+
+  // Reset previous objects
+  this->MinorTickPts->Reset();
+  this->MajorTickPts->Reset();
+  this->GridlinePts->Reset();
+  this->InnerGridlinePts->Reset();
+  this->GridpolyPts->Reset();
+
+  // As we assume that the Axis is not necessery alined to the absolute X/Y/Z
+  // axis, we will convert the absolut XYZ information to relative one
+  // using a base composed as follow (axis, u, v)
+  double uGridLength, vGridLength;
+  uGridLength = vGridLength = 0;
+  double *axisVector, *uVector, *vVector;
+  axisVector = uVector = vVector = NULL;
+  int uMult = vtkRpAxisActorMultiplierTable1[this->AxisPosition];
+  int vMult = vtkRpAxisActorMultiplierTable2[this->AxisPosition];
+
+  switch(this->AxisType)
+    {
+  case VTK_AXIS_TYPE_X:
+    uGridLength = this->GridlineYLength;
+    vGridLength = this->GridlineZLength;
+    axisVector = this->AxisBaseForX;
+    uVector = this->AxisBaseForY;
+    vVector = this->AxisBaseForZ;
+    uIndex = 1; vIndex = 2;
+    break;
+  case VTK_AXIS_TYPE_Y:
+    uGridLength = this->GridlineXLength;
+    vGridLength = this->GridlineZLength;
+    uVector = this->AxisBaseForX;
+    axisVector = this->AxisBaseForY;
+    vVector = this->AxisBaseForZ;
+    uIndex = 0; vIndex = 2;
+    break;
+  case VTK_AXIS_TYPE_Z:
+    uGridLength = this->GridlineXLength;
+    vGridLength = this->GridlineYLength;
+    uVector = this->AxisBaseForX;
+    vVector = this->AxisBaseForY;
+    axisVector = this->AxisBaseForZ;
+    uIndex = 0; vIndex = 1;
+    break;
+    }
+
+  // **************************************************************************
+  // Build Minor Ticks
+  // **************************************************************************
+  {
+  // - Initialize all points to be on the axis
+  for(i=0;i<3;i++)
+    {
+    uPointInside[i] = vPointInside[i] = uPointOutside[i] = vPointOutside[i] = p1[i];
+    deltaVector[i] = (p2[i] - p1[i]);
+    }
+  axisLength = vtkMath::Norm(deltaVector);
+  rangeScale = axisLength/(this->Range[1] - this->Range[0]);
+
+  // - Reduce the deltaVector to correspond to a tick step
+  vtkMath::Normalize(deltaVector);
+  for(i=0;i<3;i++)
+    {
+    deltaVector[i] *= this->DeltaMinor;
+    }
+
+  // - Move outside points if needed (Axis -> Outside)
+  if (this->TickLocation == VTK_TICKS_OUTSIDE || this->TickLocation == VTK_TICKS_BOTH)
+    {
+    for(i=0;i<3;i++)
+      {
+      uPointOutside[i] += uVector[i] * uMult * this->MinorTickSize;
+      vPointOutside[i] += vVector[i] * vMult * this->MinorTickSize;
+      }
+    }
+
+  // - Move inside points if needed (Axis -> Inside)
+  if (this->TickLocation == VTK_TICKS_INSIDE || this->TickLocation == VTK_TICKS_BOTH)
+    {
+    for(i=0;i<3;i++)
+      {
+      uPointInside[i] -= uVector[i] * uMult * this->MinorTickSize;
+      vPointInside[i] -= vVector[i] * vMult * this->MinorTickSize;
+      }
+    }
+
+  // - Add the initial shift if any
+  axisShift = (this->MinorRangeStart - this->Range[0])*rangeScale;
+  for(i=0;i<3;i++)
+    {
+    uPointInside[i] += axisVector[i] * axisShift;
+    vPointInside[i] += axisVector[i] * axisShift;
+    uPointOutside[i] += axisVector[i] * axisShift;
+    vPointOutside[i] += axisVector[i] * axisShift;
+    }
+
+  // - Insert tick points along the axis using the deltaVector
+  nbIterationAsDouble = axisLength / vtkMath::Norm(deltaVector);
+  nbIteration = vtkMath::Floor(nbIterationAsDouble+2*DBL_EPSILON);
+  nbIteration = (nbIteration < VTK_MAX_TICKS) ? nbIteration : VTK_MAX_TICKS;
+  for (nbTicks = 0; nbTicks < nbIteration; nbTicks++)
+    {
+    // axis/u side
+    this->MinorTickPts->InsertNextPoint(uPointInside);
+    this->MinorTickPts->InsertNextPoint(uPointOutside);
+    vtkMath::Add(deltaVector, uPointInside, uPointInside);
+    vtkMath::Add(deltaVector, uPointOutside, uPointOutside);
+    if( this->Use2DMode == 0 )
+      {
+      // axis/v side
+      this->MinorTickPts->InsertNextPoint(vPointInside);
+      this->MinorTickPts->InsertNextPoint(vPointOutside);
+      vtkMath::Add(deltaVector, vPointInside, vPointInside);
+      vtkMath::Add(deltaVector, vPointOutside, vPointOutside);
+      }
+    }
+  }
+  // **************************************************************************
+  // Build Gridline + GridPoly points + InnerGrid (Only for Orthonormal base)
+  // **************************************************************************
+  {
+  // - Initialize all points to be on the axis
+  for(i=0;i<3;i++)
+    {
+    gridPointClosest[i] = gridPointFarest[i] = gridPointU[i] = gridPointV[i] = p1[i];
+    deltaVector[i] = (p2[i] - p1[i]);
+    }
+
+  // - Reduce the deltaVector to correspond to a major tick step
+  vtkMath::Normalize(deltaVector);
+  for(i=0;i<3;i++)
+    {
+    deltaVector[i] *= this->DeltaMajor[this->AxisType];
+    }
+
+  // - Move base points
+  for(i=0;i<3;i++)
+    {
+    gridPointU[i] -= uVector[i] * uMult * uGridLength;
+    gridPointV[i] -= vVector[i] * vMult * vGridLength;
+    gridPointFarest[i] -= uVector[i] * uMult * uGridLength + vVector[i] * vMult * vGridLength;
+    }
+
+  // - Add the initial shift if any
+  axisShift = (this->MajorRangeStart - this->Range[0])*rangeScale;
+  for(i=0;i<3;i++)
+    {
+    gridPointU[i] += axisVector[i] * axisShift;
+    gridPointV[i] += axisVector[i] * axisShift;
+    gridPointFarest[i] += axisVector[i] * axisShift;
+    gridPointClosest[i] += axisVector[i] * axisShift;
+    }
+
+  // - Insert Gridlines points along the axis using the DeltaMajor vector
+  nbIterationAsDouble = (axisLength - axisShift) / vtkMath::Norm(deltaVector);
+  nbIteration = vtkMath::Floor(nbIterationAsDouble+2*FLT_EPSILON) + 1;
+  nbIteration = (nbIteration < VTK_MAX_TICKS) ? nbIteration : VTK_MAX_TICKS;
+  for (nbTicks = 0; nbTicks < nbIteration; nbTicks++)
+    {
+    // Closest U
+    this->GridlinePts->InsertNextPoint(gridPointClosest);
+    this->GridlinePts->InsertNextPoint(gridPointU);
+
+    // Farest U
+    this->GridlinePts->InsertNextPoint(gridPointFarest);
+    this->GridlinePts->InsertNextPoint(gridPointU);
+
+    // Closest V
+    this->GridlinePts->InsertNextPoint(gridPointClosest);
+    this->GridlinePts->InsertNextPoint(gridPointV);
+
+    // Farest V
+    this->GridlinePts->InsertNextPoint(gridPointFarest);
+    this->GridlinePts->InsertNextPoint(gridPointV);
+
+    // PolyPoints
+    this->GridpolyPts->InsertNextPoint(gridPointClosest);
+    this->GridpolyPts->InsertNextPoint(gridPointU);
+    this->GridpolyPts->InsertNextPoint(gridPointFarest);
+    this->GridpolyPts->InsertNextPoint(gridPointV);
+
+    // Move forward along the axis
+    for(i=0;i<3;i++)
+      {
+      gridPointClosest[i] += deltaVector[i];
+      gridPointU[i] += deltaVector[i];
+      gridPointFarest[i] += deltaVector[i];
+      gridPointV[i] += deltaVector[i];
+      }
+    }
+
+  // - Insert InnerGridLines points
+
+  // We can only handle inner grid line with orthonormal base, otherwise
+  // we would need to change the API of AxisActor which we don't want for
+  // backward compatibility.
+  if(hasOrthogonalVectorBase)
+    {
+    double axis, u, v;
+    axis = this->MajorStart[this->AxisType];
+    innerGridPointClosestU[vIndex] = this->GetBounds()[vIndex*2];
+    innerGridPointFarestU[vIndex] = this->GetBounds()[vIndex*2+1];
+    innerGridPointClosestV[uIndex] = this->GetBounds()[uIndex*2];
+    innerGridPointFarestV[uIndex] = this->GetBounds()[uIndex*2+1];
+    while (axis <= p2[this->AxisType])
+        {
+        innerGridPointClosestU[this->AxisType]
+            = innerGridPointClosestV[this->AxisType]
+            = innerGridPointFarestU[this->AxisType]
+            = innerGridPointFarestV[this->AxisType]
+            = axis;
+
+        // u lines
+        u = this->MajorStart[uIndex];
+        while (u <= p2[uIndex] && this->DeltaMajor[uIndex] > 0)
+          {
+          innerGridPointClosestU[uIndex]
+              = innerGridPointFarestU[uIndex]
+              = u;
+          this->InnerGridlinePts->InsertNextPoint(innerGridPointClosestU);
+          this->InnerGridlinePts->InsertNextPoint(innerGridPointFarestU);
+          u += this->DeltaMajor[uIndex];
+          }
+
+        // v lines
+        v = this->MajorStart[vIndex];
+        while (v <= p2[vIndex] && this->DeltaMajor[vIndex] > 0)
+          {
+          innerGridPointClosestV[vIndex]
+              = innerGridPointFarestV[vIndex]
+              = v;
+          this->InnerGridlinePts->InsertNextPoint(innerGridPointClosestV);
+          this->InnerGridlinePts->InsertNextPoint(innerGridPointFarestV);
+          v += this->DeltaMajor[vIndex];
+          }
+
+        axis += this->DeltaMajor[this->AxisType];
+        }
+    }
+  }
+  // **************************************************************************
+  // Build Major ticks
+  // **************************************************************************
+  {
+  // Delta vector is already initialized with the Major tick scale
+  // - Initialize all points to be on the axis
+  for(i=0;i<3;i++)
+    {
+    uPointInside[i] = vPointInside[i] = uPointOutside[i] = vPointOutside[i] = p1[i];
+    }
+
+  // - Move outside points if needed (Axis -> Outside)
+  if (this->TickLocation == VTK_TICKS_OUTSIDE || this->TickLocation == VTK_TICKS_BOTH)
+    {
+    for(i=0;i<3;i++)
+      {
+      uPointOutside[i] += uVector[i] * uMult * this->MajorTickSize;
+      vPointOutside[i] += vVector[i] * vMult * this->MajorTickSize;
+      }
+    }
+
+  // - Move inside points if needed (Axis -> Inside)
+  if (this->TickLocation == VTK_TICKS_INSIDE || this->TickLocation == VTK_TICKS_BOTH)
+    {
+    for(i=0;i<3;i++)
+      {
+      uPointInside[i] -= uVector[i] * uMult * this->MajorTickSize;
+      vPointInside[i] -= vVector[i] * vMult * this->MajorTickSize;
+      }
+    }
+
+  // - Add the initial shift if any
+  for(i=0;i<3;i++)
+    {
+    uPointInside[i] += axisVector[i] * axisShift;
+    vPointInside[i] += axisVector[i] * axisShift;
+    uPointOutside[i] += axisVector[i] * axisShift;
+    vPointOutside[i] += axisVector[i] * axisShift;
+    }
+
+  // - Insert tick points along the axis using the deltaVector
+  for (nbTicks = 0; nbTicks < nbIteration; nbTicks++)
+    {
+    // axis/u side
+    this->MajorTickPts->InsertNextPoint(uPointInside);
+    this->MajorTickPts->InsertNextPoint(uPointOutside);
+    vtkMath::Add(deltaVector, uPointInside, uPointInside);
+    vtkMath::Add(deltaVector, uPointOutside, uPointOutside);
+
+    // axis/v side
+    this->MajorTickPts->InsertNextPoint(vPointInside);
+    this->MajorTickPts->InsertNextPoint(vPointOutside);
+    vtkMath::Add(deltaVector, vPointInside, vPointInside);
+    vtkMath::Add(deltaVector, vPointOutside, vPointOutside);
+    }
+  }
+
+  this->BuildTickPointsTime.Modified();
+  this->LastTickLocation = this->TickLocation;
+  return true;
+}
Index: vtkvis/tags/1.8.4/vtkRpAxisActor.h
===================================================================
--- vtkvis/tags/1.8.4/vtkRpAxisActor.h	(revision 6184)
+++ vtkvis/tags/1.8.4/vtkRpAxisActor.h	(revision 6184)
@@ -0,0 +1,601 @@
+/*=========================================================================
+
+Program:   Visualization Toolkit
+Module:    vtkRpAxisActor.h
+Language:  C++
+Date:      $Date$
+Version:   $Revision$
+Thanks:    Kathleen Bonnell, B Division, Lawrence Livermore Nat'l Laboratory
+
+Copyright (c) 1993-2000 Ken Martin, Will Schroeder, Bill Lorensen
+All rights reserved.
+This software is distributed WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE.  See the above copyright notice for more information.
+=========================================================================*/
+// .NAME vtkRpAxisActor - Create an axis with tick marks and labels
+// .SECTION Description
+// vtkRpAxisActor creates an axis with tick marks, labels, and/or a title,
+// depending on the particular instance variable settings. It is assumed that
+// the axes is part of a bounding box and is orthoganal to one of the
+// coordinate axes.  To use this class, you typically specify two points
+// defining the start and end points of the line (xyz definition using
+// vtkCoordinate class), the axis type (X, Y or Z), the axis location in
+// relation to the bounding box, the bounding box, the number of labels, and
+// the data range (min,max). You can also control what parts of the axis are
+// visible including the line, the tick marks, the labels, and the title. It
+// is also possible to control gridlines, and specifiy on which 'side' the
+// tickmarks are drawn (again with respect to the underlying assumed
+// bounding box). You can also specify the label format (a printf style format).
+//
+// This class decides how to locate the labels, and how to create reasonable
+// tick marks and labels.
+//
+// Labels follow the camera so as to be legible from any viewpoint.
+//
+// The instance variables Point1 and Point2 are instances of vtkCoordinate.
+// All calculations and references are in World Coordinates.
+//
+// .SECTION Thanks
+// This class was written by:
+// Hank Childs, Kathleen Bonnell, Amy Squillacote, Brad Whitlock,
+// Eric Brugger, Claire Guilbaud, Nicolas Dolegieviez, Will Schroeder,
+// Karthik Krishnan, Aashish Chaudhary, Philippe Pebay, David Gobbi,
+// David Partyka, Utkarsh Ayachit David Cole, Francois Bertel, and Mark Olesen
+// Part of this work was supported by CEA/DIF - Commissariat a l'Energie Atomique, 
+// Centre DAM Ile-De-France, BP12, F-91297 Arpajon, France.
+//
+// .SECTION See Also
+// vtkActor vtkVectorText vtkPolyDataMapper vtkRpAxisActor2D vtkCoordinate
+
+#ifndef vtkRpAxisActor_h
+#define vtkRpAxisActor_h
+
+#include "vtkRenderingAnnotationModule.h" // For export macro
+#include "vtkActor.h"
+
+#define VTK_MAX_LABELS    200
+#define VTK_MAX_TICKS     1000
+
+#define VTK_AXIS_TYPE_X   0
+#define VTK_AXIS_TYPE_Y   1
+#define VTK_AXIS_TYPE_Z   2
+
+#define VTK_TICKS_INSIDE  0
+#define VTK_TICKS_OUTSIDE 1
+#define VTK_TICKS_BOTH    2
+
+#define VTK_AXIS_POS_MINMIN 0
+#define VTK_AXIS_POS_MINMAX 1
+#define VTK_AXIS_POS_MAXMAX 2
+#define VTK_AXIS_POS_MAXMIN 3
+
+class vtkRpAxisFollower;
+class vtkCamera;
+class vtkCoordinate;
+class vtkFollower;
+class vtkPoints;
+class vtkPolyData;
+class vtkPolyDataMapper;
+class vtkProperty2D;
+class vtkStringArray;
+class vtkTextActor;
+class vtkTextProperty;
+class vtkVectorText;
+
+class VTKRENDERINGANNOTATION_EXPORT vtkRpAxisActor : public vtkActor
+{
+ public:
+  vtkTypeMacro(vtkRpAxisActor,vtkActor);
+  void PrintSelf(ostream& os, vtkIndent indent);
+
+  // Description:
+  // Instantiate object.
+  static vtkRpAxisActor *New();
+
+  // Description:
+  // Specify the position of the first point defining the axis.
+  virtual vtkCoordinate *GetPoint1Coordinate();
+  virtual void SetPoint1(double x[3])
+  { this->SetPoint1(x[0], x[1], x[2]); }
+  virtual void SetPoint1(double x, double y, double z);
+  virtual double *GetPoint1();
+
+  // Description:
+  // Specify the position of the second point defining the axis.
+  virtual vtkCoordinate *GetPoint2Coordinate();
+  virtual void SetPoint2(double x[3])
+  { this->SetPoint2(x[0], x[1], x[2]); }
+  virtual void SetPoint2(double x, double y, double z);
+  virtual double *GetPoint2();
+
+  // Description:
+  // Specify the (min,max) axis range. This will be used in the generation
+  // of labels, if labels are visible.
+  vtkSetVector2Macro(Range,double);
+  vtkGetVectorMacro(Range,double,2);
+
+  // Description:
+  // Set or get the bounds for this Actor as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax).
+  void   SetBounds(const double bounds[6]);
+  void   SetBounds(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax);
+  double *GetBounds(void);
+  void   GetBounds(double bounds[6]);
+
+  // Description:
+  // Set/Get the format with which to print the labels on the axis.
+  vtkSetStringMacro(LabelFormat);
+  vtkGetStringMacro(LabelFormat);
+
+  // Description:
+  // Set/Get the flag that controls whether the minor ticks are visible.
+  vtkSetMacro(MinorTicksVisible, int);
+  vtkGetMacro(MinorTicksVisible, int);
+  vtkBooleanMacro(MinorTicksVisible, int);
+
+  // Description:
+  // Set/Get the title of the axis actor,
+  void SetTitle(const char *t);
+  vtkGetStringMacro(Title);
+
+  // Description:
+  // Set/Get the size of the major tick marks
+  vtkSetMacro(MajorTickSize, double);
+  vtkGetMacro(MajorTickSize, double);
+
+  // Description:
+  // Set/Get the size of the major tick marks
+  vtkSetMacro(MinorTickSize, double);
+  vtkGetMacro(MinorTickSize, double);
+
+  // Description:
+  // Set/Get the location of the ticks.
+  vtkSetClampMacro(TickLocation, int, VTK_TICKS_INSIDE, VTK_TICKS_BOTH);
+  vtkGetMacro(TickLocation, int);
+
+  void SetTickLocationToInside(void)
+  { this->SetTickLocation(VTK_TICKS_INSIDE); };
+  void SetTickLocationToOutside(void)
+  { this->SetTickLocation(VTK_TICKS_OUTSIDE); };
+  void SetTickLocationToBoth(void)
+  { this->SetTickLocation(VTK_TICKS_BOTH); };
+
+  // Description:
+  // Set/Get visibility of the axis line.
+  vtkSetMacro(AxisVisibility, int);
+  vtkGetMacro(AxisVisibility, int);
+  vtkBooleanMacro(AxisVisibility, int);
+
+  // Description:
+  // Set/Get visibility of the axis tick marks.
+  vtkSetMacro(TickVisibility, int);
+  vtkGetMacro(TickVisibility, int);
+  vtkBooleanMacro(TickVisibility, int);
+
+  // Description:
+  // Set/Get visibility of the axis labels.
+  vtkSetMacro(LabelVisibility, int);
+  vtkGetMacro(LabelVisibility, int);
+  vtkBooleanMacro(LabelVisibility, int);
+
+  // Description:
+  // Set/Get visibility of the axis title.
+  vtkSetMacro(TitleVisibility, int);
+  vtkGetMacro(TitleVisibility, int);
+  vtkBooleanMacro(TitleVisibility, int);
+
+  // Description:
+  // Set/Get the axis title text property.
+  virtual void SetTitleTextProperty(vtkTextProperty *p);
+  vtkGetObjectMacro(TitleTextProperty,vtkTextProperty);
+
+  // Description:
+  // Set/Get the axis labels text property.
+  virtual void SetLabelTextProperty(vtkTextProperty *p);
+  vtkGetObjectMacro(LabelTextProperty,vtkTextProperty);
+
+  // Description:
+  // Get/Set axis actor property (axis and its ticks)
+  void SetAxisLinesProperty(vtkProperty *);
+  vtkProperty* GetAxisLinesProperty();
+
+  // Description:
+  // Get/Set gridlines actor property (outer grid lines)
+  void SetGridlinesProperty(vtkProperty *);
+  vtkProperty* GetGridlinesProperty();
+
+  // Description:
+  // Get/Set inner gridlines actor property
+  void SetInnerGridlinesProperty(vtkProperty *);
+  vtkProperty* GetInnerGridlinesProperty();
+
+  // Description:
+  // Get/Set gridPolys actor property (grid quads)
+  void SetGridpolysProperty(vtkProperty *);
+  vtkProperty* GetGridpolysProperty();
+
+  // Description:
+  // Set/Get whether gridlines should be drawn.
+  vtkSetMacro(DrawGridlines, int);
+  vtkGetMacro(DrawGridlines, int);
+  vtkBooleanMacro(DrawGridlines, int);
+
+  // Description:
+  // Set/Get whether ONLY the gridlines should be drawn.
+  // This will only draw GridLines and will skip any other part of the rendering
+  // such as Axis/Tick/Title/...
+  vtkSetMacro(DrawGridlinesOnly, int);
+  vtkGetMacro(DrawGridlinesOnly, int);
+  vtkBooleanMacro(DrawGridlinesOnly, int);
+
+  vtkSetMacro(DrawGridlinesLocation, int);
+  vtkGetMacro(DrawGridlinesLocation, int);
+
+  // Description:
+  // Set/Get whether inner gridlines should be drawn.
+  vtkSetMacro(DrawInnerGridlines, int);
+  vtkGetMacro(DrawInnerGridlines, int);
+  vtkBooleanMacro(DrawInnerGridlines, int);
+
+  // Description:
+  // Set/Get the length to use when drawing gridlines.
+  vtkSetMacro(GridlineXLength, double);
+  vtkGetMacro(GridlineXLength, double);
+  vtkSetMacro(GridlineYLength, double);
+  vtkGetMacro(GridlineYLength, double);
+  vtkSetMacro(GridlineZLength, double);
+  vtkGetMacro(GridlineZLength, double);
+
+  // Description:
+  // Set/Get whether gridpolys should be drawn.
+  vtkSetMacro(DrawGridpolys, int);
+  vtkGetMacro(DrawGridpolys, int);
+  vtkBooleanMacro(DrawGridpolys, int);
+
+  // Description:
+  // Set/Get the type of this axis.
+  vtkSetClampMacro(AxisType, int, VTK_AXIS_TYPE_X, VTK_AXIS_TYPE_Z);
+  vtkGetMacro(AxisType, int);
+  void SetAxisTypeToX(void) { this->SetAxisType(VTK_AXIS_TYPE_X); };
+  void SetAxisTypeToY(void) { this->SetAxisType(VTK_AXIS_TYPE_Y); };
+  void SetAxisTypeToZ(void) { this->SetAxisType(VTK_AXIS_TYPE_Z); };
+
+  // Description:
+  // Set/Get the position of this axis (in relation to an an
+  // assumed bounding box).  For an x-type axis, MINMIN corresponds
+  // to the x-edge in the bounding box where Y values are minimum and
+  // Z values are minimum. For a y-type axis, MAXMIN corresponds to the
+  // y-edge where X values are maximum and Z values are minimum.
+  //
+  vtkSetClampMacro(AxisPosition, int, VTK_AXIS_POS_MINMIN, VTK_AXIS_POS_MAXMIN);
+  vtkGetMacro(AxisPosition, int);
+
+  void SetAxisPositionToMinMin(void)
+  { this->SetAxisPosition(VTK_AXIS_POS_MINMIN); };
+  void SetAxisPositionToMinMax(void)
+  { this->SetAxisPosition(VTK_AXIS_POS_MINMAX); };
+  void SetAxisPositionToMaxMax(void)
+  { this->SetAxisPosition(VTK_AXIS_POS_MAXMAX); };
+  void SetAxisPositionToMaxMin(void)
+  { this->SetAxisPosition(VTK_AXIS_POS_MAXMIN); };
+
+  // Description:
+  // Set/Get the camera for this axis.  The camera is used by the
+  // labels to 'follow' the camera and be legible from any viewpoint.
+  virtual void SetCamera(vtkCamera*);
+  vtkGetObjectMacro(Camera, vtkCamera);
+
+  // Description:
+  // Draw the axis.
+  virtual int RenderOpaqueGeometry(vtkViewport* viewport);
+  virtual int RenderTranslucentGeometry(vtkViewport* viewport);
+  virtual int RenderTranslucentPolygonalGeometry(vtkViewport* viewport);
+  virtual int RenderOverlay(vtkViewport* viewport);
+  int HasTranslucentPolygonalGeometry();
+
+  // Description:
+  // Release any graphics resources that are being consumed by this actor.
+  // The parameter window could be used to determine which graphic
+  // resources to release.
+  void ReleaseGraphicsResources(vtkWindow *);
+
+//BTX
+  double ComputeMaxLabelLength(const double [3]);
+  double ComputeTitleLength(const double [3]);
+//ETX
+  void SetLabelScale(const double scale);
+  void SetLabelScale(int labelIndex, const double scale);
+  void SetTitleScale(const double scale);
+
+  // Description:
+  // Set/Get the starting position for minor and major tick points,
+  // and the delta values that determine their spacing.
+  vtkSetMacro(MinorStart, double);
+  vtkGetMacro(MinorStart, double);
+  double GetMajorStart(int axis);
+  void SetMajorStart(int axis,double value);
+  //vtkSetMacro(MajorStart, double);
+  //vtkGetMacro(MajorStart, double);
+  vtkSetMacro(DeltaMinor, double);
+  vtkGetMacro(DeltaMinor, double);
+  double GetDeltaMajor(int axis);
+  void SetDeltaMajor(int axis,double value);
+  //vtkSetMacro(DeltaMajor, double);
+  //vtkGetMacro(DeltaMajor, double);
+
+  // Description:
+  // Set/Get the starting position for minor and major tick points on
+  // the range and the delta values that determine their spacing. The
+  // range and the position need not be identical. ie the displayed
+  // values need not match the actual positions in 3D space.
+  vtkSetMacro(MinorRangeStart, double);
+  vtkGetMacro(MinorRangeStart, double);
+  vtkSetMacro(MajorRangeStart, double);
+  vtkGetMacro(MajorRangeStart, double);
+  vtkSetMacro(DeltaRangeMinor, double);
+  vtkGetMacro(DeltaRangeMinor, double);
+  vtkSetMacro(DeltaRangeMajor, double);
+  vtkGetMacro(DeltaRangeMajor, double);
+
+//BTX
+  void SetLabels(vtkStringArray *labels);
+//ETX
+
+  void BuildAxis(vtkViewport *viewport, bool);
+
+//BTX
+  // Description:
+  // Get title actor and it is responsible for drawing
+  // title text.
+  vtkGetObjectMacro(TitleActor,  vtkRpAxisFollower);
+
+  // Description:
+  // Get label actors responsigle for drawing label text.
+  inline vtkRpAxisFollower** GetLabelActors()
+    {
+    return this->LabelActors;
+    }
+//ETX
+
+  // Description:
+  // Get total number of labels built. Once built
+  // this count does not change.
+  vtkGetMacro(NumberOfLabelsBuilt, int);
+
+  // Description:
+  // Set/Get flag whether to calculate title offset.
+  // Default is true.
+  vtkSetMacro(CalculateTitleOffset, int);
+  vtkGetMacro(CalculateTitleOffset, int);
+  vtkBooleanMacro(CalculateTitleOffset, int);
+
+  // Description:
+  // Set/Get flag whether to calculate label offset.
+  // Default is true.
+  vtkSetMacro(CalculateLabelOffset, int);
+  vtkGetMacro(CalculateLabelOffset, int);
+  vtkBooleanMacro(CalculateLabelOffset, int);
+
+  // Description:
+  // Set/Get the 2D mode
+  vtkSetMacro(Use2DMode, int);
+  vtkGetMacro(Use2DMode, int);
+
+  // Description:
+  // Set/Get the 2D mode the vertical offset for X title in 2D mode
+  vtkSetMacro(VerticalOffsetXTitle2D, double);
+  vtkGetMacro(VerticalOffsetXTitle2D, double);
+
+  // Description:
+  // Set/Get the 2D mode the horizontal offset for Y title in 2D mode
+  vtkSetMacro(HorizontalOffsetYTitle2D, double);
+  vtkGetMacro(HorizontalOffsetYTitle2D, double);
+
+  // Description:
+  // Set/Get whether title position must be saved in 2D mode
+  vtkSetMacro(SaveTitlePosition, int);
+  vtkGetMacro(SaveTitlePosition, int);
+
+  // Description:
+  // Provide real vector for non aligned axis
+  vtkSetVector3Macro(AxisBaseForX, double);
+  vtkGetVector3Macro(AxisBaseForX, double);
+
+  // Description:
+  // Provide real vector for non aligned axis
+  vtkSetVector3Macro(AxisBaseForY, double);
+  vtkGetVector3Macro(AxisBaseForY, double);
+
+  // Description:
+  // Provide real vector for non aligned axis
+  vtkSetVector3Macro(AxisBaseForZ, double);
+  vtkGetVector3Macro(AxisBaseForZ, double);
+
+  // Description:
+  // Notify the axes that is not part of a cube anymore
+  vtkSetMacro(AxisOnOrigin,int);
+  vtkGetMacro(AxisOnOrigin,int);
+
+  // Description:
+  // Set/Get the offsets used to position texts.
+  vtkSetMacro(LabelOffset, double);
+  vtkGetMacro(LabelOffset, double);
+  vtkSetMacro(TitleOffset, double);
+  vtkGetMacro(TitleOffset, double);
+  vtkSetMacro(ScreenSize, double);
+  vtkGetMacro(ScreenSize, double);
+
+ protected:
+  vtkRpAxisActor();
+  ~vtkRpAxisActor();
+
+  char  *Title;
+  double  Range[2];
+  double  LastRange[2];
+  char  *LabelFormat;
+  int    NumberOfLabelsBuilt;
+  int    MinorTicksVisible;
+  int    LastMinorTicksVisible;
+  int    TickLocation;
+
+  int    DrawGridlines;
+  int    DrawGridlinesOnly;
+  int    LastDrawGridlines;
+  int    DrawGridlinesLocation; // 0: all | 1: closest | 2: farest
+  int    LastDrawGridlinesLocation; // 0: all | 1: closest | 2: farest
+  double GridlineXLength;
+  double GridlineYLength;
+  double GridlineZLength;
+
+  int    DrawInnerGridlines;
+  int    LastDrawInnerGridlines;
+
+  int    DrawGridpolys;
+  int    LastDrawGridpolys;
+
+  int    AxisVisibility;
+  int    TickVisibility;
+  int    LastTickVisibility;
+  int    LabelVisibility;
+  int    TitleVisibility;
+
+  int    AxisType;
+  int    AxisPosition;
+  double  Bounds[6];
+
+  double AxisBaseForX[3];
+  double AxisBaseForY[3];
+  double AxisBaseForZ[3];
+
+ private:
+  vtkRpAxisActor(const vtkRpAxisActor&); // Not implemented
+  void operator=(const vtkRpAxisActor&); // Not implemented
+
+  void TransformBounds(vtkViewport *, double bnds[6]);
+
+  void BuildLabels(vtkViewport *, bool);
+  void BuildLabels2D(vtkViewport *, bool);
+  void SetLabelPositions(vtkViewport *, bool);
+  void SetLabelPositions2D(vtkViewport *, bool);
+
+  void BuildTitle(bool);
+  void BuildTitle2D(vtkViewport *viewport, bool);
+
+  void SetAxisPointsAndLines(void);
+  bool BuildTickPoints(double p1[3], double p2[3], bool force);
+
+  bool TickVisibilityChanged(void);
+  vtkProperty *NewTitleProperty();
+  vtkProperty2D *NewTitleProperty2D();
+  vtkProperty *NewLabelProperty();
+
+  bool BoundsDisplayCoordinateChanged(vtkViewport *viewport);
+
+  vtkCoordinate *Point1Coordinate;
+  vtkCoordinate *Point2Coordinate;
+
+  double  MajorTickSize;
+  double  MinorTickSize;
+
+  // For each axis (for the inner gridline generation)
+  double  MajorStart[3];
+  double  DeltaMajor[3];
+  double  MinorStart;
+  double  DeltaMinor;
+
+  // For the ticks, w.r.t to the set range
+  double  MajorRangeStart;
+  double  MinorRangeStart;
+  double  DeltaRangeMinor;
+  double  DeltaRangeMajor;
+
+  int    LastAxisPosition;
+  int    LastAxisType;
+  int    LastTickLocation;
+  double LastLabelStart;
+
+  vtkPoints         *MinorTickPts;
+  vtkPoints         *MajorTickPts;
+  vtkPoints         *GridlinePts;
+  vtkPoints         *InnerGridlinePts;
+  vtkPoints         *GridpolyPts;
+
+  vtkVectorText     *TitleVector;
+  vtkPolyDataMapper *TitleMapper;
+  vtkRpAxisFollower *TitleActor;
+  vtkTextActor      *TitleActor2D;
+  vtkTextProperty   *TitleTextProperty;
+
+  vtkVectorText     **LabelVectors;
+  vtkPolyDataMapper **LabelMappers;
+  vtkRpAxisFollower **LabelActors;
+  vtkTextActor      **LabelActors2D;
+  vtkTextProperty    *LabelTextProperty;
+
+  vtkPolyData        *AxisLines;
+  vtkPolyDataMapper  *AxisLinesMapper;
+  vtkActor           *AxisLinesActor;
+  vtkPolyData        *Gridlines;
+  vtkPolyDataMapper  *GridlinesMapper;
+  vtkActor           *GridlinesActor;
+  vtkPolyData        *InnerGridlines;
+  vtkPolyDataMapper  *InnerGridlinesMapper;
+  vtkActor           *InnerGridlinesActor;
+  vtkPolyData        *Gridpolys;
+  vtkPolyDataMapper  *GridpolysMapper;
+  vtkActor           *GridpolysActor;
+
+  vtkCamera          *Camera;
+  vtkTimeStamp        BuildTime;
+  vtkTimeStamp        BuildTickPointsTime;
+  vtkTimeStamp        BoundsTime;
+  vtkTimeStamp        LabelBuildTime;
+  vtkTimeStamp        TitleTextTime;
+
+  int                 AxisOnOrigin;
+
+  int                 AxisHasZeroLength;
+
+  int                 CalculateTitleOffset;
+  int                 CalculateLabelOffset;
+
+  // Description:
+  // Use xy-axis only when Use2DMode=1:
+  int                 Use2DMode;
+
+  // Description:
+  // Vertical offset in display coordinates for X axis title (used in 2D mode only)
+  // Default: -40
+  double              VerticalOffsetXTitle2D;
+
+  // Description:
+  // Vertical offset in display coordinates for X axis title (used in 2D mode only)
+  // Default: -50
+  double              HorizontalOffsetYTitle2D;
+
+  // Description:
+  // Save title position (used in 2D mode only):
+  // val = 0 : no need to save position (doesn't stick actors in a position)
+  // val = 1 : positions have to be saved during the next render pass
+  // val = 2 : positions are saved; use them
+  int		      SaveTitlePosition;
+
+  // Description:
+  // Constant position for the title (used in 2D mode only)
+  double	      TitleConstantPosition[2];
+
+  // Description:
+  // True if the 2D title has to be built, false otherwise
+  bool                NeedBuild2D;
+
+  double	      LastMinDisplayCoordinate[3];
+  double	      LastMaxDisplayCoordinate[3];
+
+  // Description:
+  // Offsets used to position text.
+  double ScreenSize;
+  double LabelOffset;
+  double TitleOffset;
+};
+
+#endif
Index: vtkvis/tags/1.8.4/vtkRpAxisFollower.cpp
===================================================================
--- vtkvis/tags/1.8.4/vtkRpAxisFollower.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/vtkRpAxisFollower.cpp	(revision 6184)
@@ -0,0 +1,617 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkRpAxisFollower.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#include "vtkRpAxisFollower.h"
+
+#include "vtkRpAxisActor.h"
+#include "vtkBoundingBox.h"
+#include "vtkCamera.h"
+#include "vtkCoordinate.h"
+#include "vtkMath.h"
+#include "vtkMatrix4x4.h"
+#include "vtkObjectFactory.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "vtkRenderer.h"
+#include "vtkTexture.h"
+#include "vtkTransform.h"
+
+#include <math.h>
+
+vtkStandardNewMacro(vtkRpAxisFollower);
+
+// List of vectors per axis (depending on which one needs to be
+// followed.
+// Order here is X, Y, and Z.
+// Set of two axis aligned vectors that would define the Y vector.
+// Order is MINMIN, MINMAX, MAXMAX, MAXMIN
+namespace
+{
+  const double AxisAlignedY[3][4][2][3] =
+  {
+    { {{0.0,  1.0, 0.0}, {0.0, 0.0,  1.0}},
+      {{0.0,  1.0, 0.0}, {0.0, 0.0, -1.0}},
+      {{0.0, -1.0, 0.0}, {0.0, 0.0, -1.0}},
+      {{0.0, -1.0, 0.0}, {0.0, 0.0,  1.0}}
+    },
+    {
+      {{ 1.0, 0.0, 0.0}, {0.0, 0.0,  1.0}},
+      {{ 1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}},
+      {{-1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}},
+      {{-1.0, 0.0, 0.0}, {0.0, 0.0,  1.0}}
+    },
+    {
+      {{ 1.0, 0.0, 0.0},  {0.0,  1.0, 0.0}},
+      {{ 1.0, 0.0, 0.0},  {0.0, -1.0, 0.0}},
+      {{-1.0, 0.0, 0.0},  {0.0, -1.0, 0.0}},
+      {{-1.0, 0.0, 0.0},  {0.0,  1.0, 0.0}}
+    }
+  };
+}
+
+//----------------------------------------------------------------------
+// Creates a follower with no camera set
+vtkRpAxisFollower::vtkRpAxisFollower() : vtkFollower()
+{
+  this->AutoCenter                = 1;
+
+  this->EnableDistanceLOD         = 0;
+  this->DistanceLODThreshold      = 0.80;
+
+  this->EnableViewAngleLOD        = 1;
+  this->ViewAngleLODThreshold     = 0.34;
+
+  this->ScreenOffset              = 10.0;
+
+  this->Axis                      = NULL;
+
+  this->TextUpsideDown          = -1;
+  this->VisibleAtCurrentViewAngle = -1;
+
+  this->InternalMatrix = vtkMatrix4x4::New();
+}
+
+//----------------------------------------------------------------------
+vtkRpAxisFollower::~vtkRpAxisFollower()
+{
+  this->InternalMatrix->Delete();
+}
+
+//----------------------------------------------------------------------
+void vtkRpAxisFollower::SetAxis(vtkRpAxisActor *axis)
+{
+  if(!axis)
+    {
+    vtkErrorMacro("Invalid or NULL axis\n");
+    return;
+    }
+
+  if(this->Axis != axis)
+    {
+    // \NOTE: Don't increment the ref count of axis as it could lead to
+    // circular references.
+    this->Axis = axis;
+    this->Modified();
+    }
+}
+
+//----------------------------------------------------------------------
+vtkRpAxisActor* vtkRpAxisFollower::GetAxis()
+{
+  return this->Axis.GetPointer();
+}
+
+//----------------------------------------------------------------------------
+void vtkRpAxisFollower::CalculateOrthogonalVectors(double rX[3], double rY[3],
+  double rZ[3], vtkRpAxisActor *axis, double *dop, vtkRenderer *ren)
+{
+  if(!rX || !rY || !rZ)
+    {
+    vtkErrorMacro("Invalid or NULL direction vectors\n");
+    return;
+    }
+
+  if(!axis)
+    {
+    vtkErrorMacro("Invalid or NULL axis\n");
+    return;
+    }
+
+  if(!dop)
+    {
+    vtkErrorMacro("Invalid or NULL direction of projection vector\n");
+    return;
+    }
+
+  if(!ren)
+    {
+    vtkErrorMacro("Invalid or NULL renderer\n");
+    return;
+    }
+
+  vtkMatrix4x4* cameraMatrix = this->Camera->GetViewTransformMatrix();
+
+  vtkCoordinate *c1Axis =  axis->GetPoint1Coordinate();
+  vtkCoordinate *c2Axis =  axis->GetPoint2Coordinate();
+  double *axisPt1 = c1Axis->GetComputedWorldValue(ren);
+  double *axisPt2 = c2Axis->GetComputedWorldValue(ren);
+
+  rX[0] = axisPt2[0] - axisPt1[0];
+  rX[1] = axisPt2[1] - axisPt1[1];
+  rX[2] = axisPt2[2] - axisPt1[2];
+  vtkMath::Normalize(rX);
+
+  // Get Y
+  vtkMath::Cross(rX, dop, rY);
+  vtkMath::Normalize(rY);
+
+  // Get Z
+  vtkMath::Cross(rX, rY, rZ);
+  vtkMath::Normalize(rZ);
+
+  double a[3], b[3];
+
+  // Need homogeneous points.
+  double homoPt1[4] = {axisPt1[0], axisPt1[1], axisPt1[2], 1.0};
+  double homoPt2[4] = {axisPt2[0], axisPt2[1], axisPt2[2], 1.0};
+
+  double *viewCoordinatePt1 = cameraMatrix->MultiplyDoublePoint(homoPt1);
+  a[0] = viewCoordinatePt1[0];
+  a[1] = viewCoordinatePt1[1];
+  a[2] = viewCoordinatePt1[2];
+
+  double *viewCoordinatePt2 = cameraMatrix->MultiplyDoublePoint(homoPt2);
+  b[0] = viewCoordinatePt2[0];
+  b[1] = viewCoordinatePt2[1];
+  b[2] = viewCoordinatePt2[2];
+
+  // If the text is upside down, we make a 180 rotation to keep it readable.
+  if(this->IsTextUpsideDown(a, b))
+    {
+    this->TextUpsideDown = 1;
+    rX[0] = -rX[0];
+    rX[1] = -rX[1];
+    rX[2] = -rX[2];
+    rZ[0] = -rZ[0];
+    rZ[1] = -rZ[1];
+    rZ[2] = -rZ[2];
+    }
+  else
+    {
+    this->TextUpsideDown = 0;
+    }
+}
+
+//----------------------------------------------------------------------------
+double vtkRpAxisFollower::AutoScale(vtkViewport *viewport, vtkCamera *camera,
+                                  double screenSize, double position[3])
+{
+  double newScale = 0.0;
+
+  if(!viewport)
+    {
+    std::cerr << "Invalid or NULL viewport \n";
+    return newScale;
+    }
+
+  if(!camera)
+    {
+    std::cerr << "Invalid or NULL camera \n";
+    return newScale;
+    }
+
+  if(!position)
+    {
+    std::cerr << "Invalid or NULL position \n";
+    return newScale;
+    }
+
+  double factor = 1;
+  if (viewport->GetSize()[1] > 0)
+    {
+    factor = 2.0 * screenSize
+      * tan(vtkMath::RadiansFromDegrees(camera->GetViewAngle()/2.0))
+      / viewport->GetSize()[1];
+    }
+
+    double dist = sqrt(
+          vtkMath::Distance2BetweenPoints(position,
+                                          camera->GetPosition()));
+    newScale = factor * dist;
+
+    return newScale;
+}
+
+//----------------------------------------------------------------------------
+void vtkRpAxisFollower::ComputeTransformMatrix(vtkRenderer *ren)
+{
+  if(!this->Axis)
+    {
+    vtkErrorMacro("ERROR: Invalid axis\n");
+    return;
+    }
+
+  // check whether or not need to rebuild the matrix
+  if ( this->GetMTime() > this->MatrixMTime ||
+       (this->Camera && this->Camera->GetMTime() > this->MatrixMTime) )
+    {
+    this->GetOrientation();
+    this->Transform->Push();
+    this->Transform->Identity();
+    this->Transform->PostMultiply();
+
+    double pivotPoint[3] =
+    {
+      this->Origin[0],
+      this->Origin[1],
+      this->Origin[2]
+    };
+
+    if(this->AutoCenter)
+      {
+      this->GetMapper()->GetCenter(pivotPoint);
+      }
+
+    // Move pivot point to origin
+    this->Transform->Translate(-pivotPoint[0],
+                               -pivotPoint[1],
+                               -pivotPoint[2]);
+    // Scale
+    this->Transform->Scale(this->Scale[0],
+                           this->Scale[1],
+                           this->Scale[2]);
+
+    // Rotate
+    this->Transform->RotateY(this->Orientation[1]);
+    this->Transform->RotateX(this->Orientation[0]);
+    this->Transform->RotateZ(this->Orientation[2]);
+
+    double translation[3] = {0.0, 0.0, 0.0};
+    if (this->Axis)
+      {
+      vtkMatrix4x4 *matrix = this->InternalMatrix;
+      matrix->Identity();
+      double rX[3], rY[3], rZ[3];
+
+      this->ComputeRotationAndTranlation(ren, translation, rX, rY, rZ, this->Axis);
+
+      vtkMath::Normalize(rX);
+      vtkMath::Normalize(rY);
+      vtkMath::Normalize(rZ);
+
+      matrix->Element[0][0] = rX[0];
+      matrix->Element[1][0] = rX[1];
+      matrix->Element[2][0] = rX[2];
+      matrix->Element[0][1] = rY[0];
+      matrix->Element[1][1] = rY[1];
+      matrix->Element[2][1] = rY[2];
+      matrix->Element[0][2] = rZ[0];
+      matrix->Element[1][2] = rZ[1];
+      matrix->Element[2][2] = rZ[2];
+
+      this->Transform->Concatenate(matrix);
+      }
+
+    this->Transform->Translate(this->Origin[0] + this->Position[0] + translation[0],
+                               this->Origin[1] + this->Position[1] + translation[1],
+                               this->Origin[2] + this->Position[2] + translation[2]);
+
+    // Apply user defined matrix last if there is one
+    if (this->UserMatrix)
+      {
+      this->Transform->Concatenate(this->UserMatrix);
+      }
+
+    this->Transform->PreMultiply();
+    this->Transform->GetMatrix(this->Matrix);
+    this->MatrixMTime.Modified();
+    this->Transform->Pop();
+    }
+}
+
+//-----------------------------------------------------------------------------
+void vtkRpAxisFollower::ComputeRotationAndTranlation(vtkRenderer *ren, double translation[3],
+                                                   double rX[3], double rY[3], double rZ[3],
+                                                   vtkRpAxisActor *axis)
+{
+  double autoScaleFactor =
+    this->AutoScale(ren, this->Camera, this->ScreenOffset, this->Position);
+
+  double dop[3];
+  this->Camera->GetDirectionOfProjection(dop);
+  vtkMath::Normalize(dop);
+
+  this->CalculateOrthogonalVectors(rX, rY, rZ, axis, dop, ren);
+
+  double dotVal = vtkMath::Dot(rZ, dop);
+
+  double origRy[3] = {0.0, 0.0, 0.0};
+
+  origRy[0] = rY[0];
+  origRy[1] = rY[1];
+  origRy[2] = rY[2];
+
+  // NOTE: Basically the idea here is that dotVal will be positive
+  // only when we have projection direction aligned with our z directon
+  // and when that happens it means that our Y is inverted.
+  if(dotVal > 0)
+    {
+    rY[0] = -rY[0];
+    rY[1] = -rY[1];
+    rY[2] = -rY[2];
+    }
+
+  // Check visibility at current view angle.
+  if(this->EnableViewAngleLOD)
+    {
+    this->ExecuteViewAngleVisibility(rZ);
+    }
+
+  // Since we already stored all the possible Y axes that are geometry aligned,
+  // we compare our vertical vector with these vectors and if it aligns then we
+  // translate in opposite direction.
+  int axisPosition = this->Axis->GetAxisPosition();
+
+  double dotVal1 = vtkMath::Dot(AxisAlignedY[this->Axis->GetAxisType()][axisPosition][0], origRy) ;
+  double dotVal2 = vtkMath::Dot(AxisAlignedY[this->Axis->GetAxisType()][axisPosition][1], origRy) ;
+
+  if(fabs(dotVal1) > fabs(dotVal2))
+    {
+    int sign = (dotVal1 > 0 ? -1 : 1);
+
+    translation[0] =  origRy[0] * autoScaleFactor * sign;
+    translation[1] =  origRy[1] * autoScaleFactor * sign;
+    translation[2] =  origRy[2] * autoScaleFactor * sign;
+    }
+  else
+    {
+    int sign = (dotVal2 > 0 ? -1 : 1);
+
+    translation[0] =  origRy[0] * autoScaleFactor * sign;
+    translation[1] =  origRy[1] * autoScaleFactor * sign;
+    translation[2] =  origRy[2] * autoScaleFactor * sign;
+    }
+}
+
+//----------------------------------------------------------------------
+void vtkRpAxisFollower::ComputerAutoCenterTranslation(
+  const double& vtkNotUsed(autoScaleFactor), double translation[3])
+{
+  if(!translation)
+    {
+    vtkErrorMacro("ERROR: Invalid or NULL translation\n");
+    return;
+    }
+
+  double *bounds = this->GetMapper()->GetBounds();
+
+  // Offset by half of width.
+  double halfWidth  = (bounds[1] - bounds[0]) * 0.5 * this->Scale[0];
+
+  if(this->TextUpsideDown == 1)
+    {
+    halfWidth  = -halfWidth;
+    }
+
+  if(this->Axis->GetAxisType() == VTK_AXIS_TYPE_X)
+    {
+    translation[0] = translation[0] - halfWidth;
+    }
+  else if(this->Axis->GetAxisType() == VTK_AXIS_TYPE_Y)
+    {
+    translation[1] = translation[1] - halfWidth;
+    }
+  else if(this->Axis->GetAxisType() == VTK_AXIS_TYPE_Z)
+    {
+    translation[2] = translation[2] - halfWidth;
+    }
+  else
+    {
+    // Do nothing.
+    }
+
+  return;
+}
+
+//----------------------------------------------------------------------
+int vtkRpAxisFollower::TestDistanceVisibility()
+{
+  if(!this->Camera->GetParallelProjection())
+    {
+    double cameraClippingRange[2];
+
+    this->Camera->GetClippingRange(cameraClippingRange);
+
+    // We are considering the far clip plane for evaluation. In certain
+    // odd conditions it might not work.
+    const double maxVisibleDistanceFromCamera = this->DistanceLODThreshold * (cameraClippingRange[1]);
+
+    double dist = sqrt(vtkMath::Distance2BetweenPoints(this->Camera->GetPosition(),
+                                                       this->Position));
+
+    if(dist > maxVisibleDistanceFromCamera)
+      {
+      // Need to make sure we are not looking at a flat axis and therefore should enable it anyway
+      if(this->Axis)
+        {
+        vtkBoundingBox bbox(this->Axis->GetBounds());
+        return (bbox.GetDiagonalLength() > (cameraClippingRange[1] - cameraClippingRange[0])) ? 1 : 0;
+        }
+      return 0;
+      }
+    else
+      {
+      return 1;
+      }
+    }
+  else
+    {
+    return 1;
+    }
+}
+
+//----------------------------------------------------------------------
+void vtkRpAxisFollower::ExecuteViewAngleVisibility(double normal[3])
+{
+  if(!normal)
+    {
+    vtkErrorMacro("ERROR: Invalid or NULL normal\n");
+    return;
+    }
+
+  double *cameraPos = this->Camera->GetPosition();
+  double  dir[3] = {this->Position[0] - cameraPos[0],
+                    this->Position[1] - cameraPos[1],
+                    this->Position[2] - cameraPos[2]};
+  vtkMath::Normalize(dir);
+  double dotDir = vtkMath::Dot(dir, normal);
+  if( fabs(dotDir) < this->ViewAngleLODThreshold )
+    {
+    this->VisibleAtCurrentViewAngle = 0;
+    }
+  else
+    {
+    this->VisibleAtCurrentViewAngle = 1;
+    }
+}
+
+//----------------------------------------------------------------------
+void vtkRpAxisFollower::PrintSelf(ostream& os, vtkIndent indent)
+{
+  this->Superclass::PrintSelf(os,indent);
+
+  os << indent << "AutoCenter: ("  << this->AutoCenter   << ")\n";
+  os << indent << "EnableDistanceLOD: ("   << this->EnableDistanceLOD    << ")\n";
+  os << indent << "DistanceLODThreshold: ("   << this->DistanceLODThreshold    << ")\n";
+  os << indent << "EnableViewAngleLOD: ("   << this->EnableViewAngleLOD    << ")\n";
+  os << indent << "ViewAngleLODThreshold: ("   << this->ViewAngleLODThreshold    << ")\n";
+  os << indent << "ScreenOffset: ("<< this->ScreenOffset << ")\n";
+
+  if ( this->Axis )
+    {
+    os << indent << "Axis: (" << this->Axis << ")\n";
+    }
+  else
+    {
+    os << indent << "Axis: (none)\n";
+    }
+}
+
+//----------------------------------------------------------------------
+int vtkRpAxisFollower::RenderOpaqueGeometry(vtkViewport *vp)
+{
+  if ( ! this->Mapper )
+    {
+    return 0;
+    }
+
+  if (!this->Property)
+    {
+    // force creation of a property
+    this->GetProperty();
+    }
+
+  if (this->GetIsOpaque())
+    {
+    vtkRenderer *ren = static_cast<vtkRenderer *>(vp);
+    this->Render(ren);
+    return 1;
+    }
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+int vtkRpAxisFollower::RenderTranslucentPolygonalGeometry(vtkViewport *vp)
+{
+  if ( ! this->Mapper )
+    {
+    return 0;
+    }
+
+  if (!this->Property)
+    {
+    // force creation of a property
+    this->GetProperty();
+    }
+
+  if (!this->GetIsOpaque())
+    {
+    vtkRenderer *ren = static_cast<vtkRenderer *>(vp);
+    this->Render(ren);
+    return 1;
+    }
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+void vtkRpAxisFollower::Render(vtkRenderer *ren)
+{
+  if(this->EnableDistanceLOD && !this->TestDistanceVisibility())
+    {
+    this->SetVisibility(0);
+    return;
+    }
+
+  this->Property->Render(this, ren);
+
+  this->Device->SetProperty (this->Property);
+  this->Property->Render(this, ren);
+  if (this->BackfaceProperty)
+    {
+    this->BackfaceProperty->BackfaceRender(this, ren);
+    this->Device->SetBackfaceProperty(this->BackfaceProperty);
+    }
+
+  /* render the texture */
+  if (this->Texture)
+    {
+    this->Texture->Render(ren);
+    }
+
+  // make sure the device has the same matrix
+  this->ComputeTransformMatrix(ren);
+  this->Device->SetUserMatrix(this->Matrix);
+
+  this->SetVisibility(this->VisibleAtCurrentViewAngle);
+  if(this->VisibleAtCurrentViewAngle)
+    {
+    this->Device->Render(ren,this->Mapper);
+    }
+}
+
+//----------------------------------------------------------------------
+void vtkRpAxisFollower::ShallowCopy(vtkProp *prop)
+{
+  vtkRpAxisFollower *f = vtkRpAxisFollower::SafeDownCast(prop);
+  if ( f != NULL )
+    {
+    this->SetAutoCenter(f->GetAutoCenter());
+    this->SetEnableDistanceLOD(f->GetEnableDistanceLOD());
+    this->SetDistanceLODThreshold(f->GetDistanceLODThreshold());
+    this->SetEnableViewAngleLOD(f->GetEnableViewAngleLOD());
+    this->SetViewAngleLODThreshold(f->GetViewAngleLODThreshold());
+    this->SetScreenOffset(f->GetScreenOffset());
+    this->SetAxis(f->GetAxis());
+    }
+
+  // Now do superclass
+  this->Superclass::ShallowCopy(prop);
+}
+
+bool vtkRpAxisFollower::IsTextUpsideDown( double* a, double* b )
+{
+  double angle = vtkMath::RadiansFromDegrees(this->Orientation[2]);
+  return (b[0] - a[0]) * cos(angle) - (b[1] - a[1]) * sin(angle) < 0;
+}
Index: vtkvis/tags/1.8.4/vtkRpAxisFollower.h
===================================================================
--- vtkvis/tags/1.8.4/vtkRpAxisFollower.h	(revision 6184)
+++ vtkvis/tags/1.8.4/vtkRpAxisFollower.h	(revision 6184)
@@ -0,0 +1,170 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkRpAxisFollower.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+// .NAME vtkRpAxisFollower - a subclass of vtkFollower that ensures that
+// data is always parallel to the axis defined by a vtkRpAxisActor.
+// .SECTION Description
+// vtkRpAxisFollower is a subclass of vtkFollower that always follows its
+// specified axis. More specifically it will not change its position or scale,
+// but it will continually update its orientation so that it is aliged with the
+// axis and facing at angle to the camera to provide maximum visibilty.
+// This is typically used for text labels for 3d plots.
+// .SECTION see also
+// vtkActor vtkFollower vtkCamera vtkRpAxisActor vtkCubeAxesActor
+
+
+#ifndef vtkRpAxisFollower_h
+#define vtkRpAxisFollower_h
+
+#include "vtkRenderingAnnotationModule.h" // For export macro
+#include "vtkFollower.h"
+
+#include "vtkWeakPointer.h" // For vtkWeakPointer
+
+// Forward declarations.
+class vtkRpAxisActor;
+class vtkRenderer;
+
+class VTKRENDERINGANNOTATION_EXPORT vtkRpAxisFollower : public vtkFollower
+{
+public:
+ vtkTypeMacro(vtkRpAxisFollower,vtkFollower);
+ virtual void PrintSelf(ostream& os, vtkIndent indent);
+
+ // Description:
+ // Creates a follower with no camera set
+ static vtkRpAxisFollower *New();
+
+ // Description:
+ // Set axis that needs to be followed.
+ virtual void SetAxis(vtkRpAxisActor*);
+ virtual vtkRpAxisActor* GetAxis();
+
+ // Description:
+ // Set/Get state of auto center mode where additional
+ // translation will be added to make sure the underlying
+ // geometry has its pivot point at the center of its bounds.
+ vtkSetMacro(AutoCenter, int);
+ vtkGetMacro(AutoCenter, int);
+ vtkBooleanMacro(AutoCenter, int);
+
+ // Description:
+ // Enable / disable use of distance based LOD. If enabled the actor
+ // will not be visible at a certain distance from the camera.
+ // Default is false.
+ vtkSetMacro(EnableDistanceLOD, int);
+ vtkGetMacro(EnableDistanceLOD, int);
+
+ // Description:
+ // Set distance LOD threshold (0.0 - 1.0).This determines at what fraction
+ // of camera far clip range, actor is not visible.
+ // Default is 0.80.
+ vtkSetClampMacro(DistanceLODThreshold, double, 0.0, 1.0);
+ vtkGetMacro(DistanceLODThreshold, double);
+
+ // Description:
+ // Enable / disable use of view angle based LOD. If enabled the actor
+ // will not be visible at a certain view angle.
+ // Default is true.
+ vtkSetMacro(EnableViewAngleLOD, int);
+ vtkGetMacro(EnableViewAngleLOD, int);
+
+ // Description:
+ // Set view angle LOD threshold (0.0 - 1.0).This determines at what view
+ // angle to geometry will make the geometry not visibile.
+ // Default is 0.34.
+ vtkSetClampMacro(ViewAngleLODThreshold, double, 0.0, 1.0);
+ vtkGetMacro(ViewAngleLODThreshold, double);
+
+ // Description:
+ // Set/Get the desired screen offset from the axis.
+ vtkSetMacro(ScreenOffset, double);
+ vtkGetMacro(ScreenOffset, double);
+
+ // Description:
+ // This causes the actor to be rendered. It in turn will render the actor's
+ // property, texture map and then mapper. If a property hasn't been
+ // assigned, then the actor will create one automatically.
+ virtual int RenderOpaqueGeometry(vtkViewport *viewport);
+ virtual int RenderTranslucentPolygonalGeometry(vtkViewport *viewport);
+ virtual void Render(vtkRenderer *ren);
+
+ // Description:
+ // Generate the matrix based on ivars. This method overloads its superclasses
+ // ComputeMatrix() method due to the special vtkFollower matrix operations.
+ virtual void ComputeTransformMatrix(vtkRenderer *ren);
+
+ // Description:
+ // Shallow copy of a follower. Overloads the virtual vtkProp method.
+ void ShallowCopy(vtkProp *prop);
+
+ // Description:
+ // Calculate scale factor to maintain same size of a object
+ // on the screen.
+ static double AutoScale(vtkViewport *viewport, vtkCamera * camera,
+                         double screenSize, double position[3]);
+
+protected:
+ vtkRpAxisFollower();
+ ~vtkRpAxisFollower();
+
+ void CalculateOrthogonalVectors(double Rx[3], double Ry[3], double Rz[3],
+                                 vtkRpAxisActor *axis1, double *dop,
+                                 vtkRenderer *ren);
+
+
+ void ComputeRotationAndTranlation(vtkRenderer *ren, double translation[3],
+                                   double Rx[3], double Ry[3], double Rz[3],
+                                   vtkRpAxisActor *axis);
+
+ // \NOTE: Not used as of now.
+ void ComputerAutoCenterTranslation(const double& autoScaleFactor,
+                                    double translation[3]);
+
+
+ int  TestDistanceVisibility();
+ void ExecuteViewAngleVisibility(double normal[3]);
+
+ bool IsTextUpsideDown(double* a, double* b);
+
+ int          AutoCenter;
+
+ int          EnableDistanceLOD;
+ double       DistanceLODThreshold;
+
+ int          EnableViewAngleLOD;
+ double       ViewAngleLODThreshold;
+
+ double       ScreenOffset;
+
+ vtkWeakPointer<vtkRpAxisActor> Axis;
+
+
+private:
+
+ int TextUpsideDown;
+ int VisibleAtCurrentViewAngle;
+
+ vtkRpAxisFollower(const vtkRpAxisFollower&);  // Not implemented.
+ void operator =(const vtkRpAxisFollower&);  // Not implemented.
+
+ // hide the two parameter Render() method from the user and the compiler.
+ virtual void Render(vtkRenderer *, vtkMapper *) {}
+
+ //Internal matrices to avoid New/Delete for performance reasons
+ vtkMatrix4x4 *InternalMatrix;
+
+};
+
+#endif // vtkRpAxisFollower_h
Index: vtkvis/tags/1.8.4/vtkRpCubeAxesActor.cpp
===================================================================
--- vtkvis/tags/1.8.4/vtkRpCubeAxesActor.cpp	(revision 6184)
+++ vtkvis/tags/1.8.4/vtkRpCubeAxesActor.cpp	(revision 6184)
@@ -0,0 +1,2761 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkRpCubeAxesActor.cxx
+  Thanks:    Kathleen Bonnell, B Division, Lawrence Livermore National Lab
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+ =========================================================================*/
+#include "vtkRpCubeAxesActor.h"
+
+#include "vtkRpAxisActor.h"
+#include "vtkRpAxisFollower.h"
+#include "vtkBoundingBox.h"
+#include "vtkCamera.h"
+#include "vtkCoordinate.h"
+#include "vtkFollower.h"
+#include "vtkMath.h"
+#include "vtkObjectFactory.h"
+#include "vtkProperty.h"
+#include "vtkStringArray.h"
+#include "vtkTextProperty.h"
+#include "vtkViewport.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+vtkStandardNewMacro(vtkRpCubeAxesActor);
+vtkCxxSetObjectMacro(vtkRpCubeAxesActor, Camera,vtkCamera);
+// *************************************************************************
+// Instantiate this object.
+// *************************************************************************
+vtkRpCubeAxesActor::vtkRpCubeAxesActor() : vtkActor()
+{
+  this->Bounds[0] = -1.0; this->Bounds[1] = 1.0;
+  this->Bounds[2] = -1.0; this->Bounds[3] = 1.0;
+  this->Bounds[4] = -1.0; this->Bounds[5] = 1.0;
+  for(int i=0; i < 6; ++i)
+    {
+    this->RenderedBounds[i] = this->Bounds[i];
+    }
+
+  this->OrientedBounds[0] = -1.0; this->OrientedBounds[1] = 1.0;
+  this->OrientedBounds[2] = -1.0; this->OrientedBounds[3] = 1.0;
+  this->OrientedBounds[4] = -1.0; this->OrientedBounds[5] = 1.0;
+
+  // Disable oriented bounds and Axis origin
+  this->UseOrientedBounds = this->UseAxisOrigin = 0;
+
+  // Init default axis origin
+  this->AxisOrigin[0] = this->AxisOrigin[1] = this->AxisOrigin[2] = 0.0;
+
+  // Init default axis base
+  this->AxisBaseForX[0] = this->AxisBaseForX[1] = this->AxisBaseForX[2] = 0;
+  this->AxisBaseForY[0] = this->AxisBaseForY[1] = this->AxisBaseForY[2] = 0;
+  this->AxisBaseForZ[0] = this->AxisBaseForZ[1] = this->AxisBaseForZ[2] = 0;
+  this->AxisBaseForX[0] = this->AxisBaseForY[1] = this->AxisBaseForZ[2] = 1.0;
+
+  this->RebuildAxes = true;
+
+  this->Camera = NULL;
+
+  this->FlyMode = VTK_FLY_CLOSEST_TRIAD;
+  this->GridLineLocation = VTK_GRID_LINES_ALL;
+
+  // By default enable distance based LOD
+  this->EnableDistanceLOD = 1;
+  this->DistanceLODThreshold = .8;
+
+  // By default enable view angle based LOD
+  this->EnableViewAngleLOD = 1;
+  this->ViewAngleLODThreshold = .2;
+
+  // Title and label text properties
+  for (int i = 0; i < 3; i++)
+    {
+    this->TitleTextProperty[i] = vtkTextProperty::New();
+    this->TitleTextProperty[i]->SetColor(1.,1.,1.);
+    this->TitleTextProperty[i]->SetFontFamilyToArial();
+    this->TitleTextProperty[i]->SetFontSize(18);
+    this->TitleTextProperty[i]->SetVerticalJustificationToCentered();
+    this->TitleTextProperty[i]->SetJustificationToCentered();
+
+    this->LabelTextProperty[i] = vtkTextProperty::New();
+    this->LabelTextProperty[i]->SetColor(1.,1.,1.);
+    this->LabelTextProperty[i]->SetFontFamilyToArial();
+    this->LabelTextProperty[i]->SetFontSize(14);
+    this->LabelTextProperty[i]->SetVerticalJustificationToBottom();
+    this->LabelTextProperty[i]->SetJustificationToLeft();
+    }
+
+  // Axis lines
+  this->XAxesLinesProperty = vtkProperty::New();
+  this->YAxesLinesProperty = vtkProperty::New();
+  this->ZAxesLinesProperty = vtkProperty::New();
+
+  // Outer grid lines
+  this->XAxesGridlinesProperty = vtkProperty::New();
+  this->YAxesGridlinesProperty = vtkProperty::New();
+  this->ZAxesGridlinesProperty = vtkProperty::New();
+
+  // Inner grid lines
+  this->XAxesInnerGridlinesProperty = vtkProperty::New();
+  this->YAxesInnerGridlinesProperty = vtkProperty::New();
+  this->ZAxesInnerGridlinesProperty = vtkProperty::New();
+  this->XAxesInnerGridlinesProperty->SetColor(.3,.6,.1);
+  this->YAxesInnerGridlinesProperty->SetColor(.3,.6,.1);
+  this->ZAxesInnerGridlinesProperty->SetColor(.3,.6,.1);
+
+  this->XAxesGridpolysProperty = vtkProperty::New();
+  this->YAxesGridpolysProperty = vtkProperty::New();
+  this->ZAxesGridpolysProperty = vtkProperty::New();
+  this->XAxesGridpolysProperty->SetOpacity(.6);     // Default grid polys opacity
+  this->YAxesGridpolysProperty->SetOpacity(.6);     // Default grid polys opacity
+  this->ZAxesGridpolysProperty->SetOpacity(.6);     // Default grid polys opacity
+  //this->XAxesGridpolysProperty->LightingOff();       // To be able to see the polys from high camera angles
+  //this->YAxesGridpolysProperty->LightingOff();       // To be able to see the polys from high camera angles
+  //this->ZAxesGridpolysProperty->LightingOff();       // To be able to see the polys from high camera angles
+
+  this->ScreenSize  = 10.;
+  this->LabelOffset = 20.;
+  this->TitleOffset = 20.;
+
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i] = vtkRpAxisActor::New();
+    this->XAxes[i]->SetTickVisibility(1);
+    this->XAxes[i]->SetMinorTicksVisible(1);
+    this->XAxes[i]->SetLabelVisibility(1);
+    this->XAxes[i]->SetTitleVisibility(1);
+    this->XAxes[i]->SetAxisTypeToX();
+    this->XAxes[i]->SetAxisPosition(i);
+    this->XAxes[i]->SetAxisLinesProperty(this->XAxesLinesProperty);
+    this->XAxes[i]->SetGridlinesProperty(this->XAxesGridlinesProperty);
+    this->XAxes[i]->SetInnerGridlinesProperty(this->XAxesInnerGridlinesProperty);
+    this->XAxes[i]->SetGridpolysProperty(this->XAxesGridpolysProperty);
+    this->XAxes[i]->SetLabelOffset(this->LabelOffset);
+    this->XAxes[i]->SetTitleOffset(this->TitleOffset);
+    this->XAxes[i]->SetScreenSize(this->ScreenSize);
+    this->XAxes[i]->SetCalculateTitleOffset(0);
+    this->XAxes[i]->SetCalculateLabelOffset(0);
+
+    this->YAxes[i] = vtkRpAxisActor::New();
+    this->YAxes[i]->SetTickVisibility(1);
+    this->YAxes[i]->SetMinorTicksVisible(1);
+    this->YAxes[i]->SetLabelVisibility(1);
+    this->YAxes[i]->SetTitleVisibility(1);
+    this->YAxes[i]->SetAxisTypeToY();
+    this->YAxes[i]->SetAxisPosition(i);
+    this->YAxes[i]->SetAxisLinesProperty(this->YAxesLinesProperty);
+    this->YAxes[i]->SetGridlinesProperty(this->YAxesGridlinesProperty);
+    this->YAxes[i]->SetInnerGridlinesProperty(this->YAxesInnerGridlinesProperty);
+    this->YAxes[i]->SetGridpolysProperty(this->YAxesGridpolysProperty);
+    this->YAxes[i]->SetLabelOffset(this->LabelOffset);
+    this->YAxes[i]->SetTitleOffset(this->TitleOffset);
+    this->YAxes[i]->SetScreenSize(this->ScreenSize);
+    this->YAxes[i]->SetCalculateTitleOffset(0);
+    this->YAxes[i]->SetCalculateLabelOffset(0);
+
+    this->ZAxes[i] = vtkRpAxisActor::New();
+    this->ZAxes[i]->SetTickVisibility(1);
+    this->ZAxes[i]->SetMinorTicksVisible(1);
+    this->ZAxes[i]->SetLabelVisibility(1);
+    this->ZAxes[i]->SetTitleVisibility(1);
+    this->ZAxes[i]->SetAxisTypeToZ();
+    this->ZAxes[i]->SetAxisPosition(i);
+    this->ZAxes[i]->SetAxisLinesProperty(this->ZAxesLinesProperty);
+    this->ZAxes[i]->SetGridlinesProperty(this->ZAxesGridlinesProperty);
+    this->ZAxes[i]->SetInnerGridlinesProperty(this->ZAxesInnerGridlinesProperty);
+    this->ZAxes[i]->SetGridpolysProperty(this->ZAxesGridpolysProperty);
+    this->ZAxes[i]->SetLabelOffset(this->LabelOffset);
+    this->ZAxes[i]->SetTitleOffset(this->TitleOffset);
+    this->ZAxes[i]->SetScreenSize(this->ScreenSize);
+    this->ZAxes[i]->SetCalculateTitleOffset(0);
+    this->ZAxes[i]->SetCalculateLabelOffset(0);
+
+    // Pass information to axes followers.
+    vtkRpAxisFollower* follower = this->XAxes[i]->GetTitleActor();
+    follower->SetEnableDistanceLOD( this->EnableDistanceLOD );
+    follower->SetDistanceLODThreshold( this->DistanceLODThreshold );
+    follower->SetEnableViewAngleLOD( this->EnableViewAngleLOD );
+    follower->SetViewAngleLODThreshold( this->ViewAngleLODThreshold );
+
+    follower = this->YAxes[i]->GetTitleActor();
+    follower->SetEnableDistanceLOD( this->EnableDistanceLOD );
+    follower->SetDistanceLODThreshold( this->DistanceLODThreshold );
+    follower->SetEnableViewAngleLOD( this->EnableViewAngleLOD );
+    follower->SetViewAngleLODThreshold( this->ViewAngleLODThreshold );
+
+    follower = this->ZAxes[i]->GetTitleActor();
+    follower->SetEnableDistanceLOD( this->EnableDistanceLOD );
+    follower->SetDistanceLODThreshold( this->DistanceLODThreshold );
+    follower->SetEnableViewAngleLOD( this->EnableViewAngleLOD );
+    follower->SetViewAngleLODThreshold( this->ViewAngleLODThreshold );
+    }
+
+  this->XTitle = new char[7];
+  sprintf(this->XTitle, "%s", "X-Axis");
+  this->XUnits = NULL;
+  this->YTitle = new char[7];
+  sprintf(this->YTitle, "%s", "Y-Axis");
+  this->YUnits = NULL;
+  this->ZTitle = new char[7];
+  sprintf(this->ZTitle, "%s", "Z-Axis");
+  this->ZUnits = NULL;
+
+  this->ActualXLabel = 0;
+  this->ActualYLabel = 0;
+  this->ActualZLabel = 0;
+
+  this->TickLocation = VTK_TICKS_INSIDE;
+
+  this->XAxisVisibility = 1;
+  this->YAxisVisibility = 1;
+  this->ZAxisVisibility = 1;
+
+  this->XAxisTickVisibility = 1;
+  this->YAxisTickVisibility = 1;
+  this->ZAxisTickVisibility = 1;
+
+  this->XAxisMinorTickVisibility = 1;
+  this->YAxisMinorTickVisibility = 1;
+  this->ZAxisMinorTickVisibility = 1;
+
+  this->XAxisLabelVisibility = 1;
+  this->YAxisLabelVisibility = 1;
+  this->ZAxisLabelVisibility = 1;
+
+  this->DrawXGridlines = 0;
+  this->DrawYGridlines = 0;
+  this->DrawZGridlines = 0;
+
+  this->DrawXInnerGridlines = 0;
+  this->DrawYInnerGridlines = 0;
+  this->DrawZInnerGridlines = 0;
+
+  this->DrawXGridpolys = 0;
+  this->DrawYGridpolys = 0;
+  this->DrawZGridpolys = 0;
+
+  this->XLabelFormat = new char[8];
+  sprintf(this->XLabelFormat, "%s", "%-#6.3g");
+  this->YLabelFormat = new char[8];
+  sprintf(this->YLabelFormat, "%s", "%-#6.3g");
+  this->ZLabelFormat = new char[8];
+  sprintf(this->ZLabelFormat, "%s", "%-#6.3g");
+
+  this->XAutoLabelFormat = 1;
+  this->YAutoLabelFormat = 1;
+  this->ZAutoLabelFormat = 1;
+
+  this->CornerOffset = 0.0;
+
+  this->Inertia = 1;
+
+  this->RenderCount = 0;
+
+  this->InertiaLocs[0] = this->InertiaLocs[1] = this->InertiaLocs[2] = -1;
+
+  this->RenderSomething = 0;
+
+  this->LastUseOrientedBounds = 0;
+
+  this->LastXPow = 0;
+  this->LastYPow = 0;
+  this->LastZPow = 0;
+
+  this->UserXPow = 0;
+  this->UserYPow = 0;
+  this->UserZPow = 0;
+
+  this->AutoLabelScaling = true;
+
+  this->LastXAxisDigits = 3;
+  this->LastYAxisDigits = 3;
+  this->LastZAxisDigits = 3;
+
+  this->LastXRange[0] = VTK_FLOAT_MAX;
+  this->LastXRange[1] = VTK_FLOAT_MAX;
+  this->LastYRange[0] = VTK_FLOAT_MAX;
+  this->LastYRange[1] = VTK_FLOAT_MAX;
+  this->LastZRange[0] = VTK_FLOAT_MAX;
+  this->LastZRange[1] = VTK_FLOAT_MAX;
+
+  this->LastBounds[0] = VTK_DOUBLE_MAX;
+  this->LastBounds[1] = VTK_DOUBLE_MAX;
+  this->LastBounds[2] = VTK_DOUBLE_MAX;
+  this->LastBounds[3] = VTK_DOUBLE_MAX;
+  this->LastBounds[4] = VTK_DOUBLE_MAX;
+  this->LastBounds[5] = VTK_DOUBLE_MAX;
+
+  this->LastFlyMode = -1;
+
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->RenderAxesX[i] = i;
+    this->RenderAxesY[i] = i;
+    this->RenderAxesZ[i] = i;
+    }
+  this->NumberOfAxesX = this->NumberOfAxesY = this->NumberOfAxesZ = 1;
+
+  this->MustAdjustXValue = false;
+  this->MustAdjustYValue = false;
+  this->MustAdjustZValue = false;
+
+  this->ForceXLabelReset = false;
+  this->ForceYLabelReset = false;
+  this->ForceZLabelReset = false;
+
+  this->XAxisRange[0] = VTK_DOUBLE_MAX;
+  this->XAxisRange[1] = VTK_DOUBLE_MAX;
+  this->YAxisRange[0] = VTK_DOUBLE_MAX;
+  this->YAxisRange[1] = VTK_DOUBLE_MAX;
+  this->ZAxisRange[0] = VTK_DOUBLE_MAX;
+  this->ZAxisRange[1] = VTK_DOUBLE_MAX;
+
+  for (int i = 0; i < 3; ++i)
+    {
+    this->AxisLabels[i] = NULL;
+    }
+  this->LabelScale = -1.0;
+  this->TitleScale = -1.0;
+}
+
+void vtkRpCubeAxesActor::SetUse2DMode( int val )
+{
+  for( int i = 0 ; i < NUMBER_OF_ALIGNED_AXIS ; ++ i )
+    {
+    this->XAxes[i]->SetUse2DMode( val );
+    this->YAxes[i]->SetUse2DMode( val );
+    this->ZAxes[i]->SetUse2DMode( val );
+    }
+  if( ! val )
+    {
+    this->SetZAxisVisibility( 1 );
+    }
+  else
+    {
+    this->SetZAxisVisibility( 0 );
+    }
+}
+
+int vtkRpCubeAxesActor::GetUse2DMode()
+{
+  // It is assumed that all axes have the same value
+  return this->XAxes[0]->GetUse2DMode();
+}
+
+void vtkRpCubeAxesActor::SetSaveTitlePosition( int val )
+{
+  // For 2D mode only :
+  //   val = 0: no need to save position (3D axis)
+  //   val = 1: positions have to be saved during the next render pass
+  //   val = 2: positions are saved -> use them
+  for( int i = 0 ; i < NUMBER_OF_ALIGNED_AXIS ; ++ i )
+    {
+    this->XAxes[i]->SetSaveTitlePosition( val );
+    this->YAxes[i]->SetSaveTitlePosition( val );
+    }
+}
+
+// ****************************************************************************
+vtkRpCubeAxesActor::~vtkRpCubeAxesActor()
+{
+  this->SetCamera(NULL);
+
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    if (this->XAxes[i])
+      {
+      this->XAxes[i]->Delete();
+      this->XAxes[i] = NULL;
+      }
+    if (this->YAxes[i])
+      {
+      this->YAxes[i]->Delete();
+      this->YAxes[i] = NULL;
+      }
+    if (this->ZAxes[i])
+      {
+      this->ZAxes[i]->Delete();
+      this->ZAxes[i] = NULL;
+      }
+    }
+
+  if (this->XAxesLinesProperty)
+    {
+    this->XAxesLinesProperty->Delete();
+    }
+  if (this->XAxesGridlinesProperty)
+    {
+    this->XAxesGridlinesProperty->Delete();
+    }
+  if (this->XAxesInnerGridlinesProperty)
+    {
+    this->XAxesInnerGridlinesProperty->Delete();
+    }
+  if (this->XAxesGridpolysProperty)
+    {
+    this->XAxesGridpolysProperty->Delete();
+    }
+  if (this->YAxesLinesProperty)
+    {
+    this->YAxesLinesProperty->Delete();
+    }
+  if (this->YAxesGridlinesProperty)
+    {
+    this->YAxesGridlinesProperty->Delete();
+    }
+  if (this->YAxesInnerGridlinesProperty)
+    {
+    this->YAxesInnerGridlinesProperty->Delete();
+    }
+  if (this->YAxesGridpolysProperty)
+    {
+    this->YAxesGridpolysProperty->Delete();
+    }
+  if (this->ZAxesLinesProperty)
+    {
+    this->ZAxesLinesProperty->Delete();
+    }
+  if (this->ZAxesGridlinesProperty)
+    {
+    this->ZAxesGridlinesProperty->Delete();
+    }
+  if (this->ZAxesInnerGridlinesProperty)
+    {
+    this->ZAxesInnerGridlinesProperty->Delete();
+    }
+  if (this->ZAxesGridpolysProperty)
+    {
+    this->ZAxesGridpolysProperty->Delete();
+    }
+
+  for (int i = 0; i < 3; i++)
+    {
+    if(this->TitleTextProperty[i] != NULL)
+      {
+      this->TitleTextProperty[i]->Delete();
+      }
+    this->TitleTextProperty[i] = NULL;
+
+    if(this->LabelTextProperty[i] != NULL)
+      {
+      this->LabelTextProperty[i]->Delete();
+      }
+    this->LabelTextProperty[i] = NULL;
+    }
+
+  delete [] this->XLabelFormat;
+  this->XLabelFormat = NULL;
+
+  delete [] this->YLabelFormat;
+  this->YLabelFormat = NULL;
+
+  delete [] this->ZLabelFormat;
+  this->ZLabelFormat = NULL;
+
+  delete [] this->XTitle;
+  this->XTitle = NULL;
+
+  delete [] this->YTitle;
+  this->YTitle = NULL;
+
+  delete [] this->ZTitle;
+  this->ZTitle = NULL;
+
+  delete [] this->XUnits;
+  this->XUnits = NULL;
+
+  delete [] this->YUnits;
+  this->YUnits = NULL;
+
+  delete [] this->ZUnits;
+  this->ZUnits = NULL;
+
+  delete [] this->ActualXLabel;
+  this->ActualXLabel = NULL;
+
+  delete [] this->ActualYLabel;
+  this->ActualYLabel = NULL;
+
+  delete [] this->ActualZLabel;
+  this->ActualZLabel = NULL;
+}
+
+// *************************************************************************
+// Project the bounding box and compute edges on the border of the bounding
+// cube. Determine which parts of the edges are visible via intersection
+// with the boundary of the viewport (minus borders).
+// *************************************************************************
+int vtkRpCubeAxesActor::RenderOpaqueGeometry(vtkViewport *viewport)
+{
+  static bool initialRender = true;
+  return this->RenderGeometry(
+        initialRender, viewport, true,
+        &vtkRpAxisActor::RenderOpaqueGeometry);
+}
+
+// *************************************************************************
+// Project the bounding box and compute edges on the border of the bounding
+// cube. Determine which parts of the edges are visible via intersection
+// with the boundary of the viewport (minus borders).
+// *************************************************************************
+int vtkRpCubeAxesActor::RenderTranslucentGeometry(vtkViewport *viewport)
+{
+  static bool initialRender = true;
+  return this->RenderGeometry(
+        initialRender, viewport, true,
+        &vtkRpAxisActor::RenderTranslucentGeometry);
+}
+
+// *************************************************************************
+// Project the bounding box and compute edges on the border of the bounding
+// cube. Determine which parts of the edges are visible via intersection
+// with the boundary of the viewport (minus borders).
+// *************************************************************************
+int vtkRpCubeAxesActor::RenderTranslucentPolygonalGeometry(vtkViewport *viewport)
+{
+  static bool initialRender = true;
+  return this->RenderGeometry(
+        initialRender, viewport, true,
+        &vtkRpAxisActor::RenderTranslucentPolygonalGeometry);
+}
+
+// *************************************************************************
+// RenderOverlay : render 2D annotations.
+// *************************************************************************
+int vtkRpCubeAxesActor::RenderOverlay(vtkViewport *viewport)
+{
+  static bool initialRender = true;
+  return this->RenderGeometry(
+        initialRender, viewport, false,
+        &vtkRpAxisActor::RenderOverlay);
+}
+
+// --------------------------------------------------------------------------
+int vtkRpCubeAxesActor::HasTranslucentPolygonalGeometry()
+{
+  if ((this->NumberOfAxesX > 0 &&
+       this->XAxes[0]->HasTranslucentPolygonalGeometry()) ||
+      (this->NumberOfAxesY > 0 &&
+       this->YAxes[0]->HasTranslucentPolygonalGeometry()) ||
+      (this->NumberOfAxesZ > 0 &&
+       this->ZAxes[0]->HasTranslucentPolygonalGeometry()))
+    {
+    return 1;
+    }
+
+  return 0;
+}
+
+// --------------------------------------------------------------------------
+// Do final adjustment of axes to control offset, etc.
+void vtkRpCubeAxesActor::AdjustAxes(double bounds[6],
+                                  double xCoords[NUMBER_OF_ALIGNED_AXIS][6],
+                                  double yCoords[NUMBER_OF_ALIGNED_AXIS][6],
+                                  double zCoords[NUMBER_OF_ALIGNED_AXIS][6],
+                                  double xRange[2], double yRange[2],
+                                  double zRange[2])
+{
+  xRange[0] = (this->XAxisRange[0] == VTK_DOUBLE_MAX ?
+                                  bounds[0] : this->XAxisRange[0]);
+  xRange[1] = (this->XAxisRange[1] == VTK_DOUBLE_MAX ?
+                                  bounds[1] : this->XAxisRange[1]);
+  yRange[0] = (this->YAxisRange[0] == VTK_DOUBLE_MAX ?
+                                  bounds[2] : this->YAxisRange[0]);
+  yRange[1] = (this->YAxisRange[1] == VTK_DOUBLE_MAX ?
+                                  bounds[3] : this->YAxisRange[1]);
+  zRange[0] = (this->ZAxisRange[0] == VTK_DOUBLE_MAX ?
+                                  bounds[4] : this->ZAxisRange[0]);
+  zRange[1] = (this->ZAxisRange[1] == VTK_DOUBLE_MAX ?
+                                  bounds[5] : this->ZAxisRange[1]);
+
+  const double xScale = (xRange[1] - xRange[0])/(bounds[1] - bounds[0]);
+  const double yScale = (yRange[1] - yRange[0])/(bounds[3] - bounds[2]);
+  const double zScale = (zRange[1] - zRange[0])/(bounds[5] - bounds[4]);
+
+  // Pull back the corners if specified
+  if (this->CornerOffset > 0.0)
+    {
+    for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+      {
+      double ave;
+
+      // x-axis
+      ave = (xCoords[i][0] + xCoords[i][2]) / 2.0;
+      xCoords[i][0] =
+        xCoords[i][0] - this->CornerOffset * (xCoords[i][0] - ave);
+      xCoords[i][2] =
+        xCoords[i][2] - this->CornerOffset * (xCoords[i][2] - ave);
+
+      ave = (xCoords[i][1] + xCoords[i][3]) / 2.0;
+      xCoords[i][1] =
+        xCoords[i][1] - this->CornerOffset * (xCoords[i][1] - ave);
+      xCoords[i][3] =
+        xCoords[i][3] - this->CornerOffset * (xCoords[i][3] - ave);
+
+      ave = (xRange[1] + xRange[0]) / 2.0;
+      xRange[0] = xRange[0] - this->CornerOffset * xScale * (xRange[0] - ave);
+      xRange[1] = xRange[1] - this->CornerOffset * xScale * (xRange[1] - ave);
+
+      // y-axis
+      ave = (yCoords[i][0] + yCoords[i][2]) / 2.0;
+      yCoords[i][0] =
+        yCoords[i][0] - this->CornerOffset * (yCoords[i][0] - ave);
+      yCoords[i][2] =
+        yCoords[i][2] - this->CornerOffset * (yCoords[i][2] - ave);
+
+      ave = (yCoords[i][1] + yCoords[i][3]) / 2.0;
+      yCoords[i][1] =
+        yCoords[i][1] - this->CornerOffset * (yCoords[i][1] - ave);
+      yCoords[i][3] =
+        yCoords[i][3] - this->CornerOffset * (yCoords[i][3] - ave);
+
+      ave = (yRange[1] + yRange[0]) / 2.0;
+      yRange[0] = yRange[0] - this->CornerOffset * yScale * (yRange[0] - ave);
+      yRange[1] = yRange[1] - this->CornerOffset * yScale * (yRange[1] - ave);
+
+      // z-axis
+      ave = (zCoords[i][0] + zCoords[i][2]) / 2.0;
+      zCoords[i][0] =
+        zCoords[i][0] - this->CornerOffset * (zCoords[i][0] - ave);
+      zCoords[i][2] =
+        zCoords[i][2] - this->CornerOffset * (zCoords[i][2] - ave);
+
+      ave = (zCoords[i][1] + zCoords[i][3]) / 2.0;
+      zCoords[i][1] =
+        zCoords[i][1] - this->CornerOffset * (zCoords[i][1] - ave);
+      zCoords[i][3] =
+        zCoords[i][3] - this->CornerOffset * (zCoords[i][3] - ave);
+
+      ave = (zRange[1] + zRange[0]) / 2.0;
+      zRange[0] = zRange[0] - this->CornerOffset * zScale * (zRange[0] - ave);
+      zRange[1] = zRange[1] - this->CornerOffset * zScale * (zRange[1] - ave);
+      }
+    }
+}
+
+// *************************************************************************
+// Screen size affects the screen offset as well.
+// *************************************************************************
+void vtkRpCubeAxesActor::SetScreenSize(double screenSize)
+{
+  this->ScreenSize = screenSize;
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i]->SetScreenSize(screenSize);
+    this->YAxes[i]->SetScreenSize(screenSize);
+    this->ZAxes[i]->SetScreenSize(screenSize);
+    }
+
+  this->Modified();
+}
+
+// *************************************************************************
+// Offset between labels and axis.
+// *************************************************************************
+void vtkRpCubeAxesActor::SetLabelOffset(double offset)
+{
+  this->LabelOffset = offset;
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i]->SetLabelOffset(offset);
+    this->YAxes[i]->SetLabelOffset(offset);
+    this->ZAxes[i]->SetLabelOffset(offset);
+    }
+
+  this->Modified();
+}
+
+// *************************************************************************
+// Offset between title and labels.
+// *************************************************************************
+void vtkRpCubeAxesActor::SetTitleOffset(double offset)
+{
+  this->TitleOffset = offset;
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i]->SetTitleOffset(offset);
+    this->YAxes[i]->SetTitleOffset(offset);
+    this->ZAxes[i]->SetTitleOffset(offset);
+    }
+
+  this->Modified();
+}
+
+// *************************************************************************
+// Release any graphics resources that are being consumed by this actor.
+// The parameter window could be used to determine which graphic
+// resources to release.
+// *************************************************************************
+void vtkRpCubeAxesActor::ReleaseGraphicsResources(vtkWindow *win)
+{
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i]->ReleaseGraphicsResources(win);
+    this->YAxes[i]->ReleaseGraphicsResources(win);
+    this->ZAxes[i]->ReleaseGraphicsResources(win);
+    }
+}
+
+// ******************************************************************
+void vtkRpCubeAxesActor::PrintSelf(ostream& os, vtkIndent indent)
+{
+  this->Superclass::PrintSelf(os,indent);
+
+  os << indent << "Bounds: \n";
+  os << indent << "  Xmin,Xmax: (" << this->Bounds[0] << ", "
+     << this->Bounds[1] << ")\n";
+  os << indent << "  Ymin,Ymax: (" << this->Bounds[2] << ", "
+     << this->Bounds[3] << ")\n";
+  os << indent << "  Zmin,Zmax: (" << this->Bounds[4] << ", "
+     << this->Bounds[5] << ")\n";
+
+
+  os << indent << "XAxisRange: [" << this->XAxisRange[0] << ", "
+    << this->XAxisRange[1] << "] " << endl;
+  os << indent << "YAxisRange: [" << this->YAxisRange[0] << ", "
+    << this->YAxisRange[1] << "] " << endl;
+  os << indent << "ZAxisRange: [" << this->ZAxisRange[0] << ", "
+    << this->ZAxisRange[1] << "] " << endl;
+
+  os << indent << "ScreenSize: (" << this->ScreenSize << ")\n";
+
+  if (this->Camera)
+    {
+    os << indent << "Camera:\n";
+    this->Camera->PrintSelf(os,indent.GetNextIndent());
+    }
+  else
+    {
+    os << indent << "Camera: (none)\n";
+    }
+
+  os << indent << "RebuildAxes: " << this->RebuildAxes << endl;
+
+  if (this->FlyMode == VTK_FLY_CLOSEST_TRIAD)
+    {
+    os << indent << "Fly Mode: CLOSEST_TRIAD\n";
+    }
+  else if (this->FlyMode == VTK_FLY_FURTHEST_TRIAD)
+    {
+    os << indent << "Fly Mode: FURTHEST_TRIAD\n";
+    }
+  else if (this->FlyMode == VTK_FLY_STATIC_TRIAD)
+    {
+    os << indent << "Fly Mode: STATIC_TRIAD\n";
+    }
+  else if (this->FlyMode == VTK_FLY_STATIC_EDGES)
+    {
+    os << indent << "Fly Mode: STATIC_EDGES\n";
+    }
+  else
+    {
+    os << indent << "Fly Mode: OUTER_EDGES\n";
+    }
+
+  os << indent << "EnableDistanceLOD: "
+     << ( this->EnableDistanceLOD ? "On" : "Off" ) << endl;
+  os << indent << "DistanceLODThreshold: "   << this->DistanceLODThreshold    << "\n";
+
+  os << indent << "EnableViewAngleLOD: "
+     << ( this->EnableViewAngleLOD ? "On" : "Off" ) << endl;
+  os << indent << "ViewAngleLODThreshold: "   << this->ViewAngleLODThreshold    << "\n";
+
+  os << indent << "X Axis Title: " << this->XTitle << "\n";
+  os << indent << "Y Axis Title: " << this->YTitle << "\n";
+  os << indent << "Z Axis Title: " << this->ZTitle << "\n";
+
+  os << indent << "X Axis Visibility: "
+     << (this->XAxisVisibility ? "On\n" : "Off\n");
+  os << indent << "Y Axis Visibility: "
+     << (this->YAxisVisibility ? "On\n" : "Off\n");
+  os << indent << "Z Axis Visibility: "
+     << (this->ZAxisVisibility ? "On\n" : "Off\n");
+
+  os << indent << "X Axis Label Format: " << this->XLabelFormat << "\n";
+  os << indent << "Y Axis Label Format: " << this->YLabelFormat << "\n";
+  os << indent << "Z Axis Label Format: " << this->ZLabelFormat << "\n";
+
+  os << indent << "XAutoLabelFormat: "
+     << (this->XAutoLabelFormat ? "On" : "Off") << endl;
+  os << indent << "YAutoLabelFormat: "
+     << (this->YAutoLabelFormat ? "On" : "Off") << endl;
+  os << indent << "ZAutoLabelFormat: "
+     << (this->ZAutoLabelFormat ? "On" : "Off") << endl;
+
+  os << indent << "Inertia: " << this->Inertia << "\n";
+  os << indent << "Corner Offset: " << this->CornerOffset << "\n";
+
+  os << indent << "XAxisTickVisibility: "
+     << (this->XAxisTickVisibility ? "On" : "Off") << endl;
+  os << indent << "YAxisTickVisibility: "
+     << (this->YAxisTickVisibility ? "On" : "Off") << endl;
+  os << indent << "ZAxisTickVisibility: "
+     << (this->ZAxisTickVisibility ? "On" : "Off") << endl;
+
+  os << indent << "XAxisMinorTickVisibility: "
+     << (this->XAxisMinorTickVisibility ? "On" : "Off") << endl;
+  os << indent << "YAxisMinorTickVisibility: "
+     << (this->YAxisMinorTickVisibility ? "On" : "Off") << endl;
+  os << indent << "ZAxisMinorTickVisibility: "
+     << (this->ZAxisMinorTickVisibility ? "On" : "Off") << endl;
+
+  os << indent << "XAxisLabelVisibility: "
+     << (this->XAxisLabelVisibility ? "On" : "Off") << endl;
+  os << indent << "YAxisLabelVisibility: "
+     << (this->YAxisLabelVisibility ? "On" : "Off") << endl;
+  os << indent << "ZAxisLabelVisibility: "
+     << (this->ZAxisLabelVisibility ? "On" : "Off") << endl;
+
+  os << indent << "XUnits: "
+     << (this->XUnits ? this->XUnits : "(none)") << endl;
+  os << indent << "YUnits: "
+     << (this->YUnits ? this->YUnits : "(none)") << endl;
+  os << indent << "ZUnits: "
+     << (this->ZUnits ? this->ZUnits : "(none)") << endl;
+
+  os << indent << "TickLocation: " << this->TickLocation << endl;
+
+  os << indent << "DrawXGridlines: " << this->DrawXGridlines << endl;
+  os << indent << "DrawYGridlines: " << this->DrawYGridlines << endl;
+  os << indent << "DrawZGridlines: " << this->DrawZGridlines << endl;
+
+  switch(this->GridLineLocation)
+    {
+  case VTK_GRID_LINES_ALL:
+    os << indent << "GridLineLocation: VTK_GRID_LINES_ALL (0)" << endl;
+    break;
+  case VTK_GRID_LINES_CLOSEST:
+    os << indent << "GridLineLocation: VTK_GRID_LINES_CLOSEST (1)" << endl;
+    break;
+  case VTK_GRID_LINES_FURTHEST:
+    os << indent << "GridLineLocation: VTK_GRID_LINES_FURTHEST (2)" << endl;
+    break;
+    }
+
+  os << indent << "DrawXInnerGridlines: " << this->DrawXInnerGridlines << endl;
+  os << indent << "DrawYInnerGridlines: " << this->DrawYInnerGridlines << endl;
+  os << indent << "DrawZInnerGridlines: " << this->DrawZInnerGridlines << endl;
+
+  os << indent << "DrawXGridpolys: " << this->DrawXGridpolys << endl;
+  os << indent << "DrawYGridpolys: " << this->DrawYGridpolys << endl;
+  os << indent << "DrawZGridpolys: " << this->DrawZGridpolys << endl;
+
+
+  os << indent << "UseOrientedBounds: " << this->UseOrientedBounds << endl;
+  if(this->UseOrientedBounds)
+    {
+    os << indent << "OrientedBounds: \n";
+    os << indent << "  Xmin,Xmax: (" << this->OrientedBounds[0] << ", "
+       << this->OrientedBounds[1] << ")\n";
+    os << indent << "  Ymin,Ymax: (" << this->OrientedBounds[2] << ", "
+       << this->OrientedBounds[3] << ")\n";
+    os << indent << "  Zmin,Zmax: (" << this->OrientedBounds[4] << ", "
+       << this->OrientedBounds[5] << ")\n";
+    }
+
+  os << indent << "Base: \n";
+  os << indent << "  For X: (" << this->AxisBaseForX[0] << ", "
+     << this->AxisBaseForX[1] << ", " << this->AxisBaseForX[2] << ") \n";
+  os << indent << "  For Y: (" << this->AxisBaseForY[0] << ", "
+     << this->AxisBaseForY[1] << ", " << this->AxisBaseForY[2] << ") \n";
+  os << indent << "  For Z: (" << this->AxisBaseForZ[0] << ", "
+     << this->AxisBaseForZ[1] << ", " << this->AxisBaseForZ[2] << ") \n";
+
+  os << indent << "UseAxisOrigin: " << this->UseAxisOrigin << endl;
+  if(this->UseAxisOrigin)
+    {
+    os << indent << "AxisOrigin: (" << this->AxisOrigin[0] << ", "
+       << this->AxisOrigin[1] << ", " << this->AxisOrigin[2] << ")" << endl;
+    }
+}
+
+// --------------------------------------------------------------------------
+void vtkRpCubeAxesActor::TransformBounds(vtkViewport *viewport,
+                                       const double bounds[6],
+                                       double pts[8][3])
+{
+  double x[3];
+
+  //loop over verts of bounding box
+  for ( int k = 0; k < 2; ++ k )
+    {
+    x[2] = bounds[4 + k];
+    for ( int j = 0; j < 2; ++ j )
+      {
+      x[1] = bounds[2 + j];
+      for ( int i = 0; i < 2; ++ i )
+        {
+        int idx = i + 2 * j + 4 * k;
+        x[0] = bounds[i];
+        viewport->SetWorldPoint( x[0], x[1], x[2], 1. );
+        viewport->WorldToDisplay();
+        viewport->GetDisplayPoint( pts[idx] );
+        }
+      }
+    }
+}
+
+// ***********************************************************************
+//  Calculate the size (length) of major and minor ticks,
+//  based on an average of the coordinate direction ranges.
+//  Set the necessary Axes methods with the calculated information.
+//
+//  Returns:  false if tick size not recomputed, true otherwise.
+// ***********************************************************************
+bool vtkRpCubeAxesActor::ComputeTickSize(double bounds[6])
+{
+  bool xPropsChanged = this->LabelTextProperty[0]->GetMTime() > this->BuildTime.GetMTime();
+  bool yPropsChanged = this->LabelTextProperty[1]->GetMTime() > this->BuildTime.GetMTime();
+  bool zPropsChanged = this->LabelTextProperty[2]->GetMTime() > this->BuildTime.GetMTime();
+
+  bool xRangeChanged = this->LastXRange[0] != this->XAxisRange[0] ||
+                       this->LastXRange[1] != this->XAxisRange[1];
+
+  bool yRangeChanged = this->LastYRange[0] != this->YAxisRange[0] ||
+                       this->LastYRange[1] != this->YAxisRange[1];
+
+  bool zRangeChanged = this->LastZRange[0] != this->ZAxisRange[0] ||
+                       this->LastZRange[1] != this->ZAxisRange[1];
+
+  bool boundsChanged = this->LastBounds[0] != bounds[0] ||
+                       this->LastBounds[1] != bounds[1] ||
+                       this->LastBounds[2] != bounds[2] ||
+                       this->LastBounds[3] != bounds[3] ||
+                       this->LastBounds[4] != bounds[4] ||
+                       this->LastBounds[5] != bounds[5];
+
+  if (!(xRangeChanged || yRangeChanged || zRangeChanged) &&
+      !(xPropsChanged || yPropsChanged || zPropsChanged || boundsChanged))
+    {
+    // no need to re-compute ticksize.
+    return false;
+    }
+
+  double xExt = bounds[1] - bounds[0];
+  double yExt = bounds[3] - bounds[2];
+  double zExt = bounds[5] - bounds[4];
+
+  if (xRangeChanged || boundsChanged)
+    {
+    this->AdjustTicksComputeRange(this->XAxes, bounds[0], bounds[1]);
+    this->BuildLabels(this->XAxes);
+    this->UpdateLabels(this->XAxes, 0);
+    }
+  if (yRangeChanged || boundsChanged)
+    {
+    this->AdjustTicksComputeRange(this->YAxes, bounds[2], bounds[3]);
+    this->BuildLabels(this->YAxes);
+    this->UpdateLabels(this->YAxes, 1);
+    }
+  if (zRangeChanged || boundsChanged)
+    {
+    this->AdjustTicksComputeRange(this->ZAxes, bounds[4], bounds[5]);
+    this->BuildLabels(this->ZAxes);
+    this->UpdateLabels(this->ZAxes, 2);
+    }
+
+  // We give information on deltas for the inner grid lines generation
+  for(int i = 0 ; i < NUMBER_OF_ALIGNED_AXIS ; i++)
+    {
+    for(int j = 0 ; j < 3 ; j++)
+      {
+      this->XAxes[i]->SetMajorStart(j,this->MajorStart[j]);
+      this->XAxes[i]->SetDeltaMajor(j,this->DeltaMajor[j]);
+      this->YAxes[i]->SetMajorStart(j,this->MajorStart[j]);
+      this->YAxes[i]->SetDeltaMajor(j,this->DeltaMajor[j]);
+      this->ZAxes[i]->SetMajorStart(j,this->MajorStart[j]);
+      this->ZAxes[i]->SetDeltaMajor(j,this->DeltaMajor[j]);
+      }
+    }
+
+  this->LastXRange[0] = (this->XAxisRange[0] == VTK_DOUBLE_MAX ?
+                                  bounds[0] : this->XAxisRange[0]);
+  this->LastXRange[1] = (this->XAxisRange[1] == VTK_DOUBLE_MAX ?
+                                  bounds[1] : this->XAxisRange[1]);
+  this->LastYRange[0] = (this->YAxisRange[0] == VTK_DOUBLE_MAX ?
+                                  bounds[2] : this->YAxisRange[0]);
+  this->LastYRange[1] = (this->YAxisRange[1] == VTK_DOUBLE_MAX ?
+                                  bounds[3] : this->YAxisRange[1]);
+  this->LastZRange[0] = (this->ZAxisRange[0] == VTK_DOUBLE_MAX ?
+                                  bounds[4] : this->ZAxisRange[0]);
+  this->LastZRange[1] = (this->ZAxisRange[1] == VTK_DOUBLE_MAX ?
+                                  bounds[5] : this->ZAxisRange[1]);
+  for(int i=0; i < 6; i++)
+    {
+    this->LastBounds[i] = bounds[i];
+    }
+
+  double major = 0.02 * (xExt + yExt + zExt) / 3.;
+  double minor = 0.5 * major;
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i]->SetMajorTickSize(major);
+    this->XAxes[i]->SetMinorTickSize(minor);
+
+    this->YAxes[i]->SetMajorTickSize(major);
+    this->YAxes[i]->SetMinorTickSize(minor);
+
+    this->ZAxes[i]->SetMajorTickSize(major);
+    this->ZAxes[i]->SetMinorTickSize(minor);
+
+    this->XAxes[i]->SetGridlineXLength(xExt);
+    this->XAxes[i]->SetGridlineYLength(yExt);
+    this->XAxes[i]->SetGridlineZLength(zExt);
+
+    this->YAxes[i]->SetGridlineXLength(xExt);
+    this->YAxes[i]->SetGridlineYLength(yExt);
+    this->YAxes[i]->SetGridlineZLength(zExt);
+
+    this->ZAxes[i]->SetGridlineXLength(xExt);
+    this->ZAxes[i]->SetGridlineYLength(yExt);
+    this->ZAxes[i]->SetGridlineZLength(zExt);
+    }
+  return true;
+}
+
+// ****************************************************************************
+//  Method: vtkRpCubeAxesActor::AdjustValues
+//
+//  Purpose:
+//      If the range of values is too big or too small, put them in scientific
+//      notation and changes the labels.
+//
+//  Arguments:
+//      bnds     The min/max values in each coordinate direction:
+//                 (min_x, max_x, min_y, max_y, min_z, max_x).
+//
+//  Note:       This code is partially stolen from old MeshTV code,
+//              /meshtvx/toolkit/plotgrid.c, axlab[x|y].
+//
+// ****************************************************************************
+void vtkRpCubeAxesActor::AdjustValues(const double xRange[2],
+                                    const double yRange[2],
+                                    const double zRange[2])
+{
+  int xPow, yPow, zPow;
+
+  if (AutoLabelScaling)
+    {
+    if (this->AxisLabels[0] == NULL)
+      {
+      xPow = this->LabelExponent(xRange[0], xRange[1]);
+      }
+    else
+      {
+      xPow = 0;
+      }
+    if (this->AxisLabels[1] == NULL)
+      {
+      yPow = this->LabelExponent(yRange[0], yRange[1]);
+      }
+    else
+      {
+      yPow = 0;
+      }
+    if (this->AxisLabels[2] == NULL)
+      {
+      zPow = this->LabelExponent(zRange[0], zRange[1]);
+      }
+    else
+      {
+      zPow = 0;
+      }
+    }
+  else
+    {
+    xPow = UserXPow;
+    yPow = UserYPow;
+    zPow = UserZPow;
+    }
+
+  std::string xTitle;
+  if (xPow != 0)
+    {
+    if (!this->MustAdjustXValue || this->LastXPow != xPow)
+      {
+      this->ForceXLabelReset = true;
+      }
+    else
+      {
+      this->ForceXLabelReset = false;
+      }
+    this->MustAdjustXValue = true;
+
+    std::ostringstream sstream;
+    if (XUnits == NULL || XUnits[0] == '\0')
+      {
+      sstream << this->XTitle << " (x10^" << xPow << ")";
+      }
+    else
+      {
+      sstream << this->XTitle << " (x10^" << xPow << " " << XUnits << ")";
+      }
+    xTitle = sstream.str();
+    }
+  else
+    {
+    if (this->MustAdjustXValue)
+      {
+      this->Modified();
+      this->ForceXLabelReset = true;
+      }
+    else
+      {
+      this->ForceXLabelReset = false;
+      }
+    this->MustAdjustXValue = false;
+
+    if (XUnits == NULL || XUnits[0] == '\0')
+      {
+      xTitle = this->XTitle;
+      }
+    else
+      {
+      xTitle = std::string(this->XTitle) + " (" + XUnits + ")";
+      }
+    }
+
+  std::string yTitle;
+  if (yPow != 0)
+    {
+    if (!this->MustAdjustYValue || this->LastYPow != yPow)
+      {
+      this->ForceYLabelReset = true;
+      }
+    else
+      {
+      this->ForceYLabelReset = false;
+      }
+    this->MustAdjustYValue = true;
+
+    std::ostringstream sstream;
+    if (YUnits == NULL || YUnits[0] == '\0')
+      {
+      sstream << this->YTitle << " (x10^" << yPow << ")";
+      }
+    else
+      {
+      sstream << this->YTitle << " (x10^" << yPow << " " << YUnits << ")";
+      }
+    yTitle = sstream.str();
+    }
+  else
+    {
+    if (this->MustAdjustYValue)
+      {
+      this->Modified();
+      this->ForceYLabelReset = true;
+      }
+    else
+      {
+      this->ForceYLabelReset = false;
+      }
+    this->MustAdjustYValue = false;
+    if (YUnits == NULL || YUnits[0] == '\0')
+      {
+      yTitle = this->YTitle;
+      }
+    else
+      {
+      yTitle = std::string(this->YTitle) + " (" + YUnits + ")";
+      }
+    }
+
+  std::string zTitle;
+  if (zPow != 0)
+    {
+    if (!this->MustAdjustZValue || this->LastZPow != zPow)
+      {
+      this->ForceZLabelReset = true;
+      }
+    else
+      {
+      this->ForceZLabelReset = false;
+      }
+    this->MustAdjustZValue = true;
+
+    std::ostringstream sstream;
+    if (ZUnits == NULL || ZUnits[0] == '\0')
+      {
+      sstream << this->ZTitle << " (x10^" << zPow << ")";
+      }
+    else
+      {
+      sstream << this->ZTitle << " (x10^" << zPow << " " << ZUnits << ")";
+      }
+    zTitle = sstream.str();
+    }
+  else
+    {
+    if (this->MustAdjustZValue)
+      {
+      this->Modified();
+      this->ForceZLabelReset = true;
+      }
+    else
+      {
+      this->ForceZLabelReset = false;
+      }
+    this->MustAdjustZValue = false;
+
+    if (ZUnits == NULL || ZUnits[0] == '\0')
+      {
+      zTitle = this->ZTitle;
+      }
+    else
+      {
+      zTitle = std::string(this->ZTitle) + " (" + ZUnits + ")";
+      }
+    }
+
+  this->LastXPow = xPow;
+  this->LastYPow = yPow;
+  this->LastZPow = zPow;
+
+  this->SetActualXLabel(xTitle.c_str());
+  this->SetActualYLabel(yTitle.c_str());
+  this->SetActualZLabel(zTitle.c_str());
+}
+
+// ****************************************************************************
+//  Method: vtkRpCubeAxesActor::AdjustRange
+//
+//  Purpose:
+//    If the range is small, adjust the precision of the values displayed.
+//
+//  Arguments:
+//    bnds    The minimum and maximum values in each coordinate direction
+//            (min_x, max_x, min_y, max_y, min_z, max_z).
+// ****************************************************************************
+void vtkRpCubeAxesActor::AdjustRange(const double bnds[6])
+{
+  if (this->XAutoLabelFormat)
+    {
+    double xrange[2];
+
+    xrange[0] = (this->XAxisRange[0] == VTK_DOUBLE_MAX ?
+                                    bnds[0] : this->XAxisRange[0]);
+    xrange[1] = (this->XAxisRange[1] == VTK_DOUBLE_MAX ?
+                                    bnds[1] : this->XAxisRange[1]);
+    if (this->LastXPow != 0)
+      {
+      xrange[0] /= pow(10., this->LastXPow);
+      xrange[1] /= pow(10., this->LastXPow);
+      }
+
+    int xAxisDigits = this->Digits(xrange[0], xrange[1]);
+    if (xAxisDigits != this->LastXAxisDigits)
+      {
+      char  format[16];
+      sprintf(format, "%%.%df", xAxisDigits);
+      this->SetXLabelFormat(format);
+      this->LastXAxisDigits = xAxisDigits;
+      }
+    }
+  if (this->YAutoLabelFormat)
+    {
+    double yrange[2];
+
+    yrange[0] = (this->YAxisRange[0] == VTK_DOUBLE_MAX ?
+                                    bnds[2] : this->YAxisRange[0]);
+    yrange[1] = (this->YAxisRange[1] == VTK_DOUBLE_MAX ?
+                                    bnds[3] : this->YAxisRange[1]);
+
+    if (this->LastYPow != 0)
+      {
+      yrange[0] /= pow(10., this->LastYPow);
+      yrange[1] /= pow(10., this->LastYPow);
+      }
+
+    int yAxisDigits = this->Digits(yrange[0], yrange[1]);
+    if (yAxisDigits != this->LastYAxisDigits)
+      {
+      char  format[16];
+      sprintf(format, "%%.%df", yAxisDigits);
+      this->SetYLabelFormat(format);
+      this->LastYAxisDigits = yAxisDigits;
+      }
+    }
+  if (this->ZAutoLabelFormat)
+    {
+    double zrange[2];
+
+    zrange[0] = (this->ZAxisRange[0] == VTK_DOUBLE_MAX ?
+                                    bnds[4] : this->ZAxisRange[0]);
+    zrange[1] = (this->ZAxisRange[1] == VTK_DOUBLE_MAX ?
+                                    bnds[5] : this->ZAxisRange[1]);
+
+    if (this->LastZPow != 0)
+      {
+      zrange[0] /= pow(10., this->LastZPow);
+      zrange[1] /= pow(10., this->LastZPow);
+      }
+ 
+    int zAxisDigits = this->Digits(zrange[0], zrange[1]);
+    if (zAxisDigits != this->LastZAxisDigits)
+      {
+      char  format[16];
+      sprintf(format, "%%.%df", zAxisDigits);
+      this->SetZLabelFormat(format);
+      this->LastZAxisDigits = zAxisDigits;
+      }
+    }
+}
+
+// ****************************************************************************
+//  Method: Digits
+//
+//  Purpose:
+//      Determines the appropriate number of digits for a given range.
+//
+//  Arguments:
+//      min    The minimum value in the range.
+//      max    The maximum value in the range.
+//
+//  Returns:   The appropriate number of digits.
+// ****************************************************************************
+int vtkRpCubeAxesActor::Digits(double min, double max )
+{
+  long digitsPastDecimal;
+
+  double range = max - min;
+  double pow10 = log10(range);
+  if (!vtkMath::IsFinite(pow10))
+    {
+    digitsPastDecimal = 0;
+    }
+  else
+    {
+    long ipow10 = static_cast<long>(floor(pow10));
+    digitsPastDecimal = -ipow10;
+
+    if (digitsPastDecimal < 0)
+      {
+      //
+      // The range is more than 10, but not so big we need scientific
+      // notation, we don't need to worry about decimals.
+      //
+      digitsPastDecimal = 0;
+      }
+    else
+      {
+      //
+      // We want one more than the range since there is more than one
+      // tick per decade.
+      //
+      digitsPastDecimal++;
+
+      //
+      // Anything more than 5 is just noise.  (and probably 5 is noise with
+      // doubling point if the part before the decimal is big).
+      //
+      if (digitsPastDecimal > 5)
+        {
+        digitsPastDecimal = 5;
+        }
+      }
+    }
+
+  return (int)digitsPastDecimal;
+}
+
+// ****************************************************************************
+//  Method: LabelExponent
+//
+//  Purpose:
+//      Determines the proper exponent for the min and max values.
+//
+//  Arguments:
+//      min     The minimum value along a certain axis.
+//      max     The maximum value along a certain axis.
+//
+//  Note:       This code is mostly stolen from old MeshTV code,
+//              /meshtvx/toolkit/plotgrid.c, axlab_format.
+// ****************************************************************************
+
+int vtkRpCubeAxesActor::LabelExponent(double min, double max)
+{
+  if (min == max)
+    {
+    return 0;
+    }
+
+  //
+  // Determine power of 10 to scale axis labels to.
+  //
+  double range = (fabs(min) > fabs(max) ? fabs(min) : fabs(max));
+  double pow10 = log10(range);
+
+  //
+  // Cutoffs for using scientific notation.  The following 4 variables
+  // should all be static for maximum performance but were made non-static
+  // to get around a compiler bug with the MIPSpro 7.2.1.3 compiler.
+  //
+  double eformat_cut_min = -1.5;
+  double eformat_cut_max =  3.0;
+  double cut_min = pow(10., eformat_cut_min);
+  double cut_max = pow(10., eformat_cut_max);
+  double ipow10;
+  if (range < cut_min || range > cut_max)
+    {
+    //
+    // We are going to use scientific notation and round the exponents to
+    // the nearest multiple of three.
+    //
+    ipow10 = (floor(floor(pow10)/3.))*3;
+    }
+  else
+    {
+    ipow10 = 0;
+    }
+
+  return static_cast<int>(ipow10);
+}
+
+// *************************************************************************
+//  Build the axes. Determine coordinates, position, etc.
+// *************************************************************************
+void vtkRpCubeAxesActor::BuildAxes(vtkViewport *viewport)
+{
+  if ((this->GetMTime() < this->BuildTime.GetMTime()))
+    {
+    this->AutoScale(viewport);
+    return;
+    }
+
+  this->SetNonDependentAttributes();
+
+  // Reset range in case of bounds type changed
+  if(this->LastUseOrientedBounds != this->UseOrientedBounds)
+    {
+    this->XAxisRange[0] = this->XAxisRange[1] = VTK_DOUBLE_MAX;
+    this->YAxisRange[0] = this->YAxisRange[1] = VTK_DOUBLE_MAX;
+    this->ZAxisRange[0] = this->ZAxisRange[1] = VTK_DOUBLE_MAX;
+    this->LastUseOrientedBounds = this->UseOrientedBounds;
+    }
+
+  // determine the bounds to use (input, prop, or user-defined)
+  double bounds[6];
+  if(this->UseOrientedBounds != 0)
+    {
+    this->GetOrientedBounds(bounds);
+    }
+  else
+    {
+    this->GetBounds(bounds);
+    }
+
+  // Setup the axes for plotting
+  double xCoords[NUMBER_OF_ALIGNED_AXIS][6];
+  double yCoords[NUMBER_OF_ALIGNED_AXIS][6];
+  double zCoords[NUMBER_OF_ALIGNED_AXIS][6];
+
+  // these arrays are accessed by 'location':  mm, mX, XX, or Xm.
+  int mm1[4] = { 0, 0, 1, 1 };
+  int mm2[4] = { 0, 1, 1, 0 };
+
+  // Compute axes end-points
+  int i;
+  for (i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    if(this->UseAxisOrigin == 0)
+      {
+      xCoords[i][0] = bounds[0]*this->AxisBaseForX[0] + bounds[2+mm1[i]]*this->AxisBaseForY[0] + bounds[4+mm2[i]]*this->AxisBaseForZ[0];
+      xCoords[i][1] = bounds[0]*this->AxisBaseForX[1] + bounds[2+mm1[i]]*this->AxisBaseForY[1] + bounds[4+mm2[i]]*this->AxisBaseForZ[1];
+      xCoords[i][2] = bounds[0]*this->AxisBaseForX[2] + bounds[2+mm1[i]]*this->AxisBaseForY[2] + bounds[4+mm2[i]]*this->AxisBaseForZ[2];
+      xCoords[i][3] = bounds[1]*this->AxisBaseForX[0] + bounds[2+mm1[i]]*this->AxisBaseForY[0] + bounds[4+mm2[i]]*this->AxisBaseForZ[0];
+      xCoords[i][4] = bounds[1]*this->AxisBaseForX[1] + bounds[2+mm1[i]]*this->AxisBaseForY[1] + bounds[4+mm2[i]]*this->AxisBaseForZ[1];
+      xCoords[i][5] = bounds[1]*this->AxisBaseForX[2] + bounds[2+mm1[i]]*this->AxisBaseForY[2] + bounds[4+mm2[i]]*this->AxisBaseForZ[2];
+      }
+    else
+      {
+      xCoords[i][0] = bounds[0]*this->AxisBaseForX[0] + this->AxisOrigin[1]*this->AxisBaseForY[0] + this->AxisOrigin[2]*this->AxisBaseForZ[0];
+      xCoords[i][1] = bounds[0]*this->AxisBaseForX[1] + this->AxisOrigin[1]*this->AxisBaseForY[1] + this->AxisOrigin[2]*this->AxisBaseForZ[1];
+      xCoords[i][2] = bounds[0]*this->AxisBaseForX[2] + this->AxisOrigin[1]*this->AxisBaseForY[2] + this->AxisOrigin[2]*this->AxisBaseForZ[2];
+      xCoords[i][3] = bounds[1]*this->AxisBaseForX[0] + this->AxisOrigin[1]*this->AxisBaseForY[0] + this->AxisOrigin[2]*this->AxisBaseForZ[0];
+      xCoords[i][4] = bounds[1]*this->AxisBaseForX[1] + this->AxisOrigin[1]*this->AxisBaseForY[1] + this->AxisOrigin[2]*this->AxisBaseForZ[1];
+      xCoords[i][5] = bounds[1]*this->AxisBaseForX[2] + this->AxisOrigin[1]*this->AxisBaseForY[2] + this->AxisOrigin[2]*this->AxisBaseForZ[2];
+      }
+
+    if(this->UseAxisOrigin == 0)
+      {
+      yCoords[i][0] = bounds[2]*this->AxisBaseForY[0] + bounds[0+mm1[i]]*this->AxisBaseForX[0] + bounds[4+mm2[i]]*this->AxisBaseForZ[0];
+      yCoords[i][1] = bounds[2]*this->AxisBaseForY[1] + bounds[0+mm1[i]]*this->AxisBaseForX[1] + bounds[4+mm2[i]]*this->AxisBaseForZ[1];
+      yCoords[i][2] = bounds[2]*this->AxisBaseForY[2] + bounds[0+mm1[i]]*this->AxisBaseForX[2] + bounds[4+mm2[i]]*this->AxisBaseForZ[2];
+      yCoords[i][3] = bounds[3]*this->AxisBaseForY[0] + bounds[0+mm1[i]]*this->AxisBaseForX[0] + bounds[4+mm2[i]]*this->AxisBaseForZ[0];
+      yCoords[i][4] = bounds[3]*this->AxisBaseForY[1] + bounds[0+mm1[i]]*this->AxisBaseForX[1] + bounds[4+mm2[i]]*this->AxisBaseForZ[1];
+      yCoords[i][5] = bounds[3]*this->AxisBaseForY[2] + bounds[0+mm1[i]]*this->AxisBaseForX[2] + bounds[4+mm2[i]]*this->AxisBaseForZ[2];
+      }
+    else
+      {
+      yCoords[i][0] = bounds[2]*this->AxisBaseForY[0] + this->AxisOrigin[0]*this->AxisBaseForX[0] + this->AxisOrigin[2]*this->AxisBaseForZ[0];
+      yCoords[i][1] = bounds[2]*this->AxisBaseForY[1] + this->AxisOrigin[0]*this->AxisBaseForX[1] + this->AxisOrigin[2]*this->AxisBaseForZ[1];
+      yCoords[i][2] = bounds[2]*this->AxisBaseForY[2] + this->AxisOrigin[0]*this->AxisBaseForX[2] + this->AxisOrigin[2]*this->AxisBaseForZ[2];
+      yCoords[i][3] = bounds[3]*this->AxisBaseForY[0] + this->AxisOrigin[0]*this->AxisBaseForX[0] + this->AxisOrigin[2]*this->AxisBaseForZ[0];
+      yCoords[i][4] = bounds[3]*this->AxisBaseForY[1] + this->AxisOrigin[0]*this->AxisBaseForX[1] + this->AxisOrigin[2]*this->AxisBaseForZ[1];
+      yCoords[i][5] = bounds[3]*this->AxisBaseForY[2] + this->AxisOrigin[0]*this->AxisBaseForX[2] + this->AxisOrigin[2]*this->AxisBaseForZ[2];
+      }
+
+    if(this->UseAxisOrigin == 0)
+      {
+      zCoords[i][0] = bounds[4]*this->AxisBaseForZ[0] + bounds[0+mm1[i]]*this->AxisBaseForX[0] + bounds[2+mm2[i]]*this->AxisBaseForY[0];
+      zCoords[i][1] = bounds[4]*this->AxisBaseForZ[1] + bounds[0+mm1[i]]*this->AxisBaseForX[1] + bounds[2+mm2[i]]*this->AxisBaseForY[1];
+      zCoords[i][2] = bounds[4]*this->AxisBaseForZ[2] + bounds[0+mm1[i]]*this->AxisBaseForX[2] + bounds[2+mm2[i]]*this->AxisBaseForY[2];
+      zCoords[i][3] = bounds[5]*this->AxisBaseForZ[0] + bounds[0+mm1[i]]*this->AxisBaseForX[0] + bounds[2+mm2[i]]*this->AxisBaseForY[0];
+      zCoords[i][4] = bounds[5]*this->AxisBaseForZ[1] + bounds[0+mm1[i]]*this->AxisBaseForX[1] + bounds[2+mm2[i]]*this->AxisBaseForY[1];
+      zCoords[i][5] = bounds[5]*this->AxisBaseForZ[2] + bounds[0+mm1[i]]*this->AxisBaseForX[2] + bounds[2+mm2[i]]*this->AxisBaseForY[2];
+      }
+    else
+      {
+      zCoords[i][0] = bounds[4]*this->AxisBaseForZ[0] + this->AxisOrigin[0]*this->AxisBaseForX[0] + this->AxisOrigin[1]*this->AxisBaseForY[0];
+      zCoords[i][1] = bounds[4]*this->AxisBaseForZ[1] + this->AxisOrigin[0]*this->AxisBaseForX[1] + this->AxisOrigin[1]*this->AxisBaseForY[1];
+      zCoords[i][2] = bounds[4]*this->AxisBaseForZ[2] + this->AxisOrigin[0]*this->AxisBaseForX[2] + this->AxisOrigin[1]*this->AxisBaseForY[2];
+      zCoords[i][3] = bounds[5]*this->AxisBaseForZ[0] + this->AxisOrigin[0]*this->AxisBaseForX[0] + this->AxisOrigin[1]*this->AxisBaseForY[0];
+      zCoords[i][4] = bounds[5]*this->AxisBaseForZ[1] + this->AxisOrigin[0]*this->AxisBaseForX[1] + this->AxisOrigin[1]*this->AxisBaseForY[1];
+      zCoords[i][5] = bounds[5]*this->AxisBaseForZ[2] + this->AxisOrigin[0]*this->AxisBaseForX[2] + this->AxisOrigin[1]*this->AxisBaseForY[2];
+      }
+    }
+
+  double xRange[2], yRange[2], zRange[2];
+
+  // this method sets the coords, offsets, and ranges if necessary.
+  this->AdjustAxes(bounds, xCoords, yCoords, zCoords, xRange, yRange, zRange);
+
+  // adjust for sci. notation if necessary
+  // May set a flag for each axis specifying that label values should
+  // be scaled, may change title of each axis, may change label format.
+  this->AdjustValues(xRange, yRange, zRange);
+  this->AdjustRange(bounds);
+
+  // Prepare axes for rendering with user-definable options
+  for (i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i]->SetAxisOnOrigin(this->UseAxisOrigin);
+    this->XAxes[i]->GetPoint1Coordinate()->SetValue(xCoords[i][0],
+                                                    xCoords[i][1],
+                                                    xCoords[i][2]);
+    this->XAxes[i]->GetPoint2Coordinate()->SetValue(xCoords[i][3],
+                                                    xCoords[i][4],
+                                                    xCoords[i][5]);
+    this->YAxes[i]->SetAxisOnOrigin(this->UseAxisOrigin);
+    this->YAxes[i]->GetPoint1Coordinate()->SetValue(yCoords[i][0],
+                                                    yCoords[i][1],
+                                                    yCoords[i][2]);
+    this->YAxes[i]->GetPoint2Coordinate()->SetValue(yCoords[i][3],
+                                                    yCoords[i][4],
+                                                    yCoords[i][5]);
+    this->ZAxes[i]->SetAxisOnOrigin(this->UseAxisOrigin);
+    this->ZAxes[i]->GetPoint1Coordinate()->SetValue(zCoords[i][0],
+                                                    zCoords[i][1],
+                                                    zCoords[i][2]);
+    this->ZAxes[i]->GetPoint2Coordinate()->SetValue(zCoords[i][3],
+                                                    zCoords[i][4],
+                                                    zCoords[i][5]);
+
+    this->XAxes[i]->SetRange(xRange[0], xRange[1]);
+    this->YAxes[i]->SetRange(yRange[0], yRange[1]);
+    this->ZAxes[i]->SetRange(zRange[0], zRange[1]);
+
+    this->XAxes[i]->SetTitle(this->ActualXLabel);
+    this->YAxes[i]->SetTitle(this->ActualYLabel);
+    this->ZAxes[i]->SetTitle(this->ActualZLabel);
+    }
+
+  bool ticksRecomputed = this->ComputeTickSize(bounds);
+
+  //
+  // Labels are built during ComputeTickSize. if
+  // ticks were not recomputed, but we need a label
+  // reset, then build the labels here.
+  //
+  if (!ticksRecomputed)
+    {
+    if (this->ForceXLabelReset)
+      {
+      this->BuildLabels(this->XAxes);
+      this->UpdateLabels(this->XAxes, 0);
+      }
+    if (this->ForceYLabelReset)
+      {
+      this->BuildLabels(this->YAxes);
+      this->UpdateLabels(this->YAxes, 1);
+      }
+    if (this->ForceZLabelReset)
+      {
+      this->BuildLabels(this->ZAxes);
+      this->UpdateLabels(this->ZAxes, 2);
+      }
+    }
+
+  if (ticksRecomputed || this->ForceXLabelReset || this->ForceYLabelReset ||
+      this->ForceZLabelReset)
+    {
+    // labels were re-built, need to recompute the scale.
+    double center[3];
+
+    center[0] = (this->Bounds[1] - this->Bounds[0]) * 0.5;
+    center[1] = (this->Bounds[3] - this->Bounds[2]) * 0.5;
+    center[2] = (this->Bounds[5] - this->Bounds[4]) * 0.5;
+
+    double lenX = this->XAxes[0]->ComputeMaxLabelLength(center);
+    double lenY = this->YAxes[0]->ComputeMaxLabelLength(center);
+    double lenZ = this->ZAxes[0]->ComputeMaxLabelLength(center);
+    double lenTitleX = this->XAxes[0]->ComputeTitleLength(center);
+    double lenTitleY = this->YAxes[0]->ComputeTitleLength(center);
+    double lenTitleZ = this->ZAxes[0]->ComputeTitleLength(center);
+    double maxLabelLength = this->MaxOf(lenX, lenY, lenZ, 0.);
+    double maxTitleLength = this->MaxOf(lenTitleX, lenTitleY, lenTitleZ, 0.);
+    double bWidth  = this->Bounds[1] - this->Bounds[0];
+    double bHeight = this->Bounds[3] - this->Bounds[2];
+
+    double bLength = sqrt(bWidth*bWidth + bHeight*bHeight);
+
+    double target = bLength *0.04;
+    this->LabelScale = 1.;
+    if (maxLabelLength != 0.)
+      {
+      this->LabelScale = target / maxLabelLength;
+      }
+    target = bLength *0.10;
+    this->TitleScale = 1.;
+    if (maxTitleLength != 0.)
+      {
+      this->TitleScale = target / maxTitleLength;
+      }
+
+    //
+    // Allow a bit bigger title if we have units, otherwise
+    // the title may be too small to read.
+    //
+    if (XUnits != NULL && XUnits[0] != '\0')
+      {
+      this->TitleScale *= 2;
+      }
+
+    for (i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+      {
+      this->XAxes[i]->SetLabelScale(this->LabelScale);
+      this->YAxes[i]->SetLabelScale(this->LabelScale);
+      this->ZAxes[i]->SetLabelScale(this->LabelScale);
+      this->XAxes[i]->SetTitleScale(this->TitleScale);
+      this->YAxes[i]->SetTitleScale(this->TitleScale);
+      this->ZAxes[i]->SetTitleScale(this->TitleScale);
+
+      // Need to build the axis again prior to calling AutoScale so
+      // that labels are positioned accordingly.
+      this->XAxes[i]->BuildAxis(viewport, true);
+      this->YAxes[i]->BuildAxis(viewport, true);
+      this->ZAxes[i]->BuildAxis(viewport, true);
+      }
+    }
+
+  // Scale appropriately.
+  this->AutoScale(viewport);
+
+  this->RenderSomething = 1;
+  this->BuildTime.Modified();
+  this->LastFlyMode = this->FlyMode;
+}
+
+// *************************************************************************
+//  Sends attributes to each vtkRpAxisActor.  Only sets those that are
+//  not dependent upon viewport changes, and thus do not need to be set
+//  very often.
+// *************************************************************************
+void vtkRpCubeAxesActor::SetNonDependentAttributes()
+{
+  vtkProperty *prop = this->GetProperty();
+  prop->SetAmbient(1.0);
+  prop->SetDiffuse(0.0);
+
+  // Make sure our Axis Base is normalized
+  vtkMath::Normalize(this->AxisBaseForX);
+  vtkMath::Normalize(this->AxisBaseForY);
+  vtkMath::Normalize(this->AxisBaseForZ);
+
+  // Manage custome grid visibility location if FLY and STATIC axis
+  int gridLocationBasedOnAxis = (this->GridLineLocation == VTK_GRID_LINES_ALL)
+      ? VTK_GRID_LINES_ALL : VTK_GRID_LINES_CLOSEST;
+
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    this->XAxes[i]->SetAxisPosition(i);
+    this->XAxes[i]->SetAxisBaseForX(this->AxisBaseForX);
+    this->XAxes[i]->SetAxisBaseForY(this->AxisBaseForY);
+    this->XAxes[i]->SetAxisBaseForZ(this->AxisBaseForZ);
+    this->XAxes[i]->SetCamera(this->Camera);
+    this->XAxes[i]->SetProperty(prop);
+    this->XAxes[i]->SetTitleTextProperty(this->TitleTextProperty[0]);
+    this->XAxes[i]->SetLabelTextProperty(this->LabelTextProperty[0]);
+    this->XAxes[i]->SetAxisLinesProperty(this->XAxesLinesProperty);
+    this->XAxes[i]->SetGridlinesProperty(this->XAxesGridlinesProperty);
+    this->XAxes[i]->SetGridpolysProperty(this->XAxesGridpolysProperty);
+    this->XAxes[i]->SetTickLocation(this->TickLocation);
+    this->XAxes[i]->SetDrawGridlines(this->DrawXGridlines);
+    this->XAxes[i]->SetDrawGridlinesLocation(gridLocationBasedOnAxis);
+    this->XAxes[i]->SetDrawInnerGridlines(this->DrawXInnerGridlines);
+    this->XAxes[i]->SetDrawGridpolys(this->DrawXGridpolys);
+    this->XAxes[i]->SetBounds(this->Bounds);
+    this->XAxes[i]->SetAxisVisibility(this->XAxisVisibility);
+    this->XAxes[i]->SetLabelVisibility(this->XAxisLabelVisibility);
+    this->XAxes[i]->SetTitleVisibility(this->XAxisLabelVisibility);
+    this->XAxes[i]->SetTickVisibility(this->XAxisTickVisibility);
+    this->XAxes[i]->SetMinorTicksVisible(this->XAxisMinorTickVisibility);
+
+    this->YAxes[i]->SetAxisPosition(i);
+    this->YAxes[i]->SetAxisBaseForX(this->AxisBaseForX);
+    this->YAxes[i]->SetAxisBaseForY(this->AxisBaseForY);
+    this->YAxes[i]->SetAxisBaseForZ(this->AxisBaseForZ);
+    this->YAxes[i]->SetCamera(this->Camera);
+    this->YAxes[i]->SetProperty(prop);
+    this->YAxes[i]->SetTitleTextProperty(this->TitleTextProperty[1]);
+    this->YAxes[i]->SetLabelTextProperty(this->LabelTextProperty[1]);
+    this->YAxes[i]->SetAxisLinesProperty(this->YAxesLinesProperty);
+    this->YAxes[i]->SetGridlinesProperty(this->YAxesGridlinesProperty);
+    this->YAxes[i]->SetGridpolysProperty(this->YAxesGridpolysProperty);
+    this->YAxes[i]->SetTickLocation(this->TickLocation);
+    this->YAxes[i]->SetDrawGridlines(this->DrawYGridlines);
+    this->YAxes[i]->SetDrawGridlinesLocation(gridLocationBasedOnAxis);
+    this->YAxes[i]->SetDrawInnerGridlines(this->DrawYInnerGridlines);
+    this->YAxes[i]->SetDrawGridpolys(this->DrawYGridpolys);
+    this->YAxes[i]->SetBounds(this->Bounds);
+    this->YAxes[i]->SetAxisVisibility(this->YAxisVisibility);
+    this->YAxes[i]->SetLabelVisibility(this->YAxisLabelVisibility);
+    this->YAxes[i]->SetTitleVisibility(this->YAxisLabelVisibility);
+    this->YAxes[i]->SetTickVisibility(this->YAxisTickVisibility);
+    this->YAxes[i]->SetMinorTicksVisible(this->YAxisMinorTickVisibility);
+
+    this->ZAxes[i]->SetAxisPosition(i);
+    this->ZAxes[i]->SetAxisBaseForX(this->AxisBaseForX);
+    this->ZAxes[i]->SetAxisBaseForY(this->AxisBaseForY);
+    this->ZAxes[i]->SetAxisBaseForZ(this->AxisBaseForZ);
+    this->ZAxes[i]->SetCamera(this->Camera);
+    this->ZAxes[i]->SetProperty(prop);
+    this->ZAxes[i]->SetTitleTextProperty(this->TitleTextProperty[2]);
+    this->ZAxes[i]->SetLabelTextProperty(this->LabelTextProperty[2]);
+    this->ZAxes[i]->SetAxisLinesProperty(this->ZAxesLinesProperty);
+    this->ZAxes[i]->SetGridlinesProperty(this->ZAxesGridlinesProperty);
+    this->ZAxes[i]->SetGridpolysProperty(this->ZAxesGridpolysProperty);
+    this->ZAxes[i]->SetTickLocation(this->TickLocation);
+    this->ZAxes[i]->SetDrawGridlines(this->DrawZGridlines);
+    this->ZAxes[i]->SetDrawGridlinesLocation(gridLocationBasedOnAxis);
+    this->ZAxes[i]->SetDrawInnerGridlines(this->DrawZInnerGridlines);
+    this->ZAxes[i]->SetDrawGridpolys(this->DrawZGridpolys);
+    this->ZAxes[i]->SetBounds(this->Bounds);
+    this->ZAxes[i]->SetAxisVisibility(this->ZAxisVisibility);
+    this->ZAxes[i]->SetLabelVisibility(this->ZAxisLabelVisibility);
+    this->ZAxes[i]->SetTitleVisibility(this->ZAxisLabelVisibility);
+    this->ZAxes[i]->SetTickVisibility(this->ZAxisTickVisibility);
+    this->ZAxes[i]->SetMinorTicksVisible(this->ZAxisMinorTickVisibility);
+    }
+}
+
+// Static variable describes locations in cube, relative to the type
+// of axis:  mm for an X-axis means the x-edge at min-y and min-z.
+// mX for a Y-axis means the y-edge at min-x and max-z, and so on.
+
+enum {mm = 0, mX, XX, Xm };
+//
+// For CLOSEST_TRIAD, and FURTHEST_TRIAD, this variable determines
+// which locations in the cube each 'Major' axis should take.
+//
+static int vtkRpCubeAxesActorTriads[8][3] = {
+  {mm,mm,mm}, {mm,Xm,Xm}, {Xm,mm,mX}, {Xm,Xm,XX},
+  {mX,mX,mm}, {mX,XX,Xm}, {XX,mX,mX}, {XX,XX,XX}};
+static int vtkRpCubeAxesActorConn[8][3] = {{1,2,4}, {0,3,5}, {3,0,6}, {2,1,7},
+                                         {5,6,0}, {4,7,1}, {7,4,2}, {6,5,3}};
+
+// *************************************************************************
+// Determine which of the axes in each coordinate direction actually should
+// be rendered.  For STATIC FlyMode, all axes are rendered.  For other
+// FlyModes, either 1 or 2 per coordinate direction are rendered.
+// *************************************************************************
+void vtkRpCubeAxesActor::DetermineRenderAxes(vtkViewport *viewport)
+{
+  double bounds[6];
+  double pts[8][3];
+  int i = 0, closestIdx = -1, furtherstIdx = -1;
+  int xloc = 0, yloc = 0, zloc = 0;
+
+  // Make sure we start with only one axis by default, then we might extend it
+  this->NumberOfAxesX = this->NumberOfAxesY = this->NumberOfAxesZ = 1;
+
+  // Compute relevant axis points only if a axis/grid visibility change based
+  // on the viewpoint
+  if( !( this->GridLineLocation == VTK_GRID_LINES_ALL
+         && ( this->FlyMode == VTK_FLY_STATIC_EDGES
+              || this->FlyMode == VTK_FLY_STATIC_TRIAD)))
+    {
+    // determine the bounds to use (input, prop, or user-defined)
+    this->GetBounds(bounds);
+    this->TransformBounds(viewport, bounds, pts);
+    }
+
+  // Check closest point if needed
+  if( this->GridLineLocation == VTK_GRID_LINES_CLOSEST
+      || this->FlyMode == VTK_FLY_CLOSEST_TRIAD )
+    {
+    closestIdx = this->FindClosestAxisIndex(pts);
+    }
+
+  // Check furtherst point if needed
+  if( this->GridLineLocation == VTK_GRID_LINES_FURTHEST
+      || this->FlyMode == VTK_FLY_FURTHEST_TRIAD )
+    {
+    furtherstIdx = this->FindFurtherstAxisIndex(pts);
+    }
+
+  // Manage fast static axis visibility
+  if (this->FlyMode == VTK_FLY_STATIC_EDGES || this->FlyMode == VTK_FLY_STATIC_TRIAD)
+    {
+    if(this->FlyMode == VTK_FLY_STATIC_EDGES)
+      {
+      this->NumberOfAxesX = this->NumberOfAxesY = this->NumberOfAxesZ
+          = NUMBER_OF_ALIGNED_AXIS;
+      }
+
+    for (i = 0; i < this->NumberOfAxesX; i++)
+      {
+      this->RenderAxesX[i] = this->RenderAxesY[i] = this->RenderAxesZ[i] = i;
+      }
+
+    this->UpdateGridLineVisibility(
+          (this->GridLineLocation == VTK_GRID_LINES_CLOSEST)
+          ? closestIdx : furtherstIdx);
+    return;
+    }
+
+
+  // Take into account the inertia. Process only so often.
+  if (this->RenderCount++ == 0 || !(this->RenderCount % this->Inertia))
+    {
+    if (this->FlyMode == VTK_FLY_CLOSEST_TRIAD)
+      {
+      xloc = vtkRpCubeAxesActorTriads[closestIdx][0];
+      yloc = vtkRpCubeAxesActorTriads[closestIdx][1];
+      zloc = vtkRpCubeAxesActorTriads[closestIdx][2];
+      }
+    else if (this->FlyMode == VTK_FLY_FURTHEST_TRIAD)
+      {
+      xloc = vtkRpCubeAxesActorTriads[furtherstIdx][0];
+      yloc = vtkRpCubeAxesActorTriads[furtherstIdx][1];
+      zloc = vtkRpCubeAxesActorTriads[furtherstIdx][2];
+      }
+    else // else boundary edges fly mode
+      {
+      this->FindBoundaryEdge(xloc, yloc, zloc, pts);
+      }
+
+    this->InertiaLocs[0] = xloc;
+    this->InertiaLocs[1] = yloc;
+    this->InertiaLocs[2] = zloc;
+    } // inertia
+  else
+    {
+    // Do not change anything, use locations from last render
+    xloc = this->InertiaLocs[0];
+    yloc = this->InertiaLocs[1];
+    zloc = this->InertiaLocs[2];
+    }
+
+  // Set axes to be rendered
+  this->RenderAxesX[0] = xloc % NUMBER_OF_ALIGNED_AXIS;
+  this->RenderAxesY[0] = yloc % NUMBER_OF_ALIGNED_AXIS;
+  this->RenderAxesZ[0] = zloc % NUMBER_OF_ALIGNED_AXIS;
+
+  // Manage grid visibility (can increase the number of axis to render)
+  this->UpdateGridLineVisibility(
+        (this->GridLineLocation == VTK_GRID_LINES_CLOSEST)
+        ? closestIdx : furtherstIdx);
+}
+
+// --------------------------------------------------------------------------
+double vtkRpCubeAxesActor::MaxOf(double a, double b)
+{
+  return (a > b ? a : b);
+}
+
+// --------------------------------------------------------------------------
+double vtkRpCubeAxesActor::MaxOf(double a, double b, double c, double d)
+{
+  return this->MaxOf(this->MaxOf(a, b), this->MaxOf(c, d));
+}
+
+// --------------------------------------------------------------------------
+inline double vtkRpCubeAxesActor::FFix(double value)
+{
+  int ivalue = static_cast<int>(value);
+  return ivalue;
+}
+
+inline int vtkRpCubeAxesActor::FRound(double value)
+{
+  return value <= 0.5 ? static_cast<int>(this->FFix(value)) : static_cast<int>(this->FFix(value) + 1);
+}
+
+inline int vtkRpCubeAxesActor::GetNumTicks(double range, double fxt)
+{
+  // Find the number of integral points in the interval.
+  double fnt  = range/fxt;
+  fnt  = this->FFix(fnt);
+  return this->FRound(fnt);
+}
+
+// --------------------------------------------------------------------------
+inline double vtkRpCubeAxesActor::FSign(double value, double sign)
+{
+  value = fabs(value);
+  if (sign < 0.)
+    {
+    value *= -1.;
+    }
+  return value;
+}
+
+// *******************************************************************
+// Method: vtkRpCubeAxesActor::AdjustTicksComputeRange
+//
+// Purpose: Sets private members controlling the number and position
+//          of ticks.
+//
+// Arguments:
+//   inRange   The range for this axis.
+// *******************************************************************
+
+void vtkRpCubeAxesActor::AdjustTicksComputeRange(vtkRpAxisActor *axes[NUMBER_OF_ALIGNED_AXIS],
+    double boundsMin, double boundsMax)
+{
+  double sortedRange[2], range;
+  double fxt;
+  double div, major, minor;
+  double majorStart, minorStart;
+  int numTicks;
+  double *inRange = axes[0]->GetRange();
+  vtkStringArray* customizedLabels = NULL;
+
+  sortedRange[0] = inRange[0] < inRange[1] ? inRange[0] : inRange[1];
+  sortedRange[1] = inRange[0] > inRange[1] ? inRange[0] : inRange[1];
+
+  range = sortedRange[1] - sortedRange[0];
+
+  // Find the integral points.
+  double pow10 = log10(range);
+
+  // Build in numerical tolerance
+  if (pow10 != 0.)
+    {
+    double eps = 10.0e-10;
+    pow10 = this->FSign((fabs(pow10) + eps), pow10);
+    }
+
+  // FFix move you in the wrong direction if pow10 is negative.
+  if (pow10 < 0.)
+    {
+    pow10 = pow10 - 1.;
+    }
+
+  fxt = pow(10., this->FFix(pow10));
+
+  numTicks = this->GetNumTicks(range, fxt);
+
+  div = 1.;
+  if (numTicks < 5)
+    {
+    div = 2.;
+    }
+  if (numTicks <= 2)
+    {
+    div = 5.;
+    }
+
+  // If there aren't enough major tick points in this decade, use the next
+  // decade.
+  major = fxt;
+  if (div != 1.)
+    {
+    major /= div;
+    }
+
+  int axis = 0;
+  switch(axes[0]->GetAxisType())
+  {
+  case VTK_AXIS_TYPE_X:
+    axis = 0;
+    break;
+  case VTK_AXIS_TYPE_Y:
+    axis = 1;
+    break;
+  case VTK_AXIS_TYPE_Z:
+    axis = 2;
+    break;
+  }
+  customizedLabels = this->AxisLabels[axis];
+
+  if (customizedLabels == NULL)
+    {
+    // Figure out the first major tick locations, relative to the
+    // start of the axis.
+    if (this->FFix(sortedRange[0]*(1./major)) == sortedRange[0]*(1./major))
+      {
+      majorStart = sortedRange[0];
+      }
+    else if (sortedRange[0] <= 0.)
+      {
+      majorStart = major*(this->FFix(sortedRange[0]*(1./major)) + 0.);
+      }
+    else
+      {
+      majorStart = major*(this->FFix(sortedRange[0]*(1./major)) + 1.);
+      }
+    }
+  else
+    {
+      // If we have custom labels, they are supposed to be uniformly distributed
+      // inside the values range.
+      majorStart = sortedRange[0];
+      numTicks = this->GetNumTicks(range, major);
+      int labelsCount = customizedLabels->GetNumberOfValues();
+      if (numTicks > labelsCount)
+        {
+        major = range / (labelsCount - 1.);
+        }
+    }
+
+  minor = major / 10.;
+  // Figure out the first minor tick locations, relative to the
+  // start of the axis.
+  if (this->FFix(sortedRange[0]*(1./minor)) == sortedRange[0]*(1./minor))
+    {
+    minorStart = sortedRange[0];
+    }
+  else if (sortedRange[0] <= 0.)
+    {
+    minorStart = minor*(this->FFix(sortedRange[0]*(1./minor)) + 0.);
+    }
+  else
+    {
+    minorStart = minor*(this->FFix(sortedRange[0]*(1./minor)) + 1.);
+    }
+
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    axes[i]->SetMinorRangeStart(minorStart);
+    axes[i]->SetMajorRangeStart(majorStart);
+    axes[i]->SetDeltaRangeMinor(minor);
+    axes[i]->SetDeltaRangeMajor(major);
+    }
+
+  double t;
+  t = (minorStart - sortedRange[0])/range;
+  minorStart = t * boundsMax + (1-t) * boundsMin;
+  t = (majorStart - sortedRange[0])/range;
+  majorStart = t * boundsMax + (1-t) * boundsMin;
+  const double scale = (boundsMax - boundsMin) / range;
+  minor *= scale;
+  major *= scale;
+
+  // Set major start and delta for the corresponding cube axis
+  switch(axes[0]->GetAxisType())
+    {
+    case VTK_AXIS_TYPE_X:
+      this->MajorStart[0] = majorStart;
+      this->DeltaMajor[0] = major;
+      break;
+    case VTK_AXIS_TYPE_Y:
+      this->MajorStart[1] = majorStart;
+      this->DeltaMajor[1] = major;
+      break;
+    case VTK_AXIS_TYPE_Z:
+      this->MajorStart[2] = majorStart;
+      this->DeltaMajor[2] = major;
+      break;
+    }
+
+  // Set major and minor starts and deltas for all underlying axes
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    axes[i]->SetMinorStart(minorStart);
+    axes[i]->SetMajorStart(axes[0]->GetAxisType(), majorStart);
+
+    axes[i]->SetDeltaMinor(minor);
+    axes[i]->SetDeltaMajor(axes[0]->GetAxisType(), major);
+    }
+}
+
+// ****************************************************************
+void vtkRpCubeAxesActor::AutoScale(vtkViewport *viewport)
+{
+  // Current implementation only for perspective projections.
+  this->AutoScale(viewport, this->XAxes);
+  this->AutoScale(viewport, this->YAxes);
+  this->AutoScale(viewport, this->ZAxes);
+}
+
+// ****************************************************************
+void vtkRpCubeAxesActor::AutoScale(vtkViewport *viewport, vtkRpAxisActor *axis[NUMBER_OF_ALIGNED_AXIS])
+{
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; ++i)
+    {
+    double newTitleScale
+      = this->AutoScale(viewport,
+                        this->ScreenSize,
+                        axis[i]->GetTitleActor()->GetPosition());
+
+    axis[i]->SetTitleScale(newTitleScale);
+
+    // Now labels.
+    vtkRpAxisFollower** labelActors = axis[i]->GetLabelActors();
+
+    for(int j = 0; j < axis[i]->GetNumberOfLabelsBuilt(); ++j)
+      {
+      double newLabelScale
+        = this->AutoScale(viewport,
+                          this->ScreenSize,
+                          labelActors[j]->GetPosition());
+
+      axis[i]->SetLabelScale(j, newLabelScale);
+      }
+    }
+}
+
+// ****************************************************************
+double vtkRpCubeAxesActor::AutoScale(vtkViewport *viewport, double screenSize,
+                                   double position[3])
+{
+  double factor = 1;
+  if (viewport->GetSize()[1] > 0)
+    {
+    factor = 2.0 * screenSize
+      * tan(vtkMath::RadiansFromDegrees(this->Camera->GetViewAngle()/2.0))
+      / viewport->GetSize()[1];
+    }
+
+    double dist = sqrt(
+          vtkMath::Distance2BetweenPoints(position,
+                                          this->Camera->GetPosition()));
+    double newScale = factor * dist;
+
+    return newScale;
+}
+
+// ****************************************************************
+//  Determine what the labels should be and set them in each axis.
+// ****************************************************************
+void vtkRpCubeAxesActor::BuildLabels(vtkRpAxisActor *axes[NUMBER_OF_ALIGNED_AXIS])
+{
+  char label[64];
+  int labelCount = 0;
+  double deltaMajor = axes[0]->GetDeltaMajor(axes[0]->GetAxisType());
+  double val = axes[0]->GetMajorRangeStart();
+  double p2[3], p1[3];
+  axes[0]->GetPoint1Coordinate()->GetValue(p1);
+  axes[0]->GetPoint2Coordinate()->GetValue(p2);
+  double *range = axes[0]->GetRange();
+  double axis[3] = { p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2] };
+  double axisLength = vtkMath::Norm(axis);
+  double extents = range[1] - range[0];
+  double rangeScale = axisLength / extents;
+  double labelCountAsDouble = (axisLength - (val-range[0])*rangeScale) / deltaMajor;
+  bool mustAdjustValue = 0;
+  int lastPow = 0;
+  int axisIndex = 0;
+  vtkStringArray* customizedLabels = NULL;
+
+  vtkStringArray *labels = vtkStringArray::New();
+  const char *format = "%s";
+  switch (axes[0]->GetAxisType())
+    {
+    case VTK_AXIS_TYPE_X:
+      axisIndex = 0;
+      format = this->XLabelFormat;
+      mustAdjustValue = this->MustAdjustXValue;
+      lastPow = this->LastXPow;
+      break;
+    case VTK_AXIS_TYPE_Y:
+      axisIndex = 1;
+      format = this->YLabelFormat;
+      mustAdjustValue = this->MustAdjustYValue;
+      lastPow = this->LastYPow;
+      break;
+    case VTK_AXIS_TYPE_Z:
+      axisIndex = 2;
+      format = this->ZLabelFormat;
+      mustAdjustValue = this->MustAdjustZValue;
+      lastPow = this->LastZPow;
+      break;
+    }
+  customizedLabels = this->AxisLabels[axisIndex];
+  // figure out how many labels we need:
+  if(extents == 0 || vtkMath::IsNan(labelCountAsDouble))
+    {
+    labelCount = 0;
+    }
+  else
+    {
+    labelCount = vtkMath::Floor(labelCountAsDouble+2*FLT_EPSILON) + 1;
+    }
+
+  labels->SetNumberOfValues(labelCount);
+
+  if (customizedLabels == NULL)
+    {
+    // Convert deltaMajor from world coord to range scale
+    deltaMajor = extents * deltaMajor/axisLength;
+
+    double scaleFactor = 1.;
+    if (lastPow != 0)
+      {
+      scaleFactor = 1.0/pow(10., lastPow);
+      }
+
+    for (int i = 0; i < labelCount; i++)
+      {
+      if (fabs(val) < 0.01 && extents > 1)
+        {
+        // We just happened to fall at something near zero and the range is
+        // large, so set it to zero to avoid ugliness.
+        val = 0.;
+        }
+      if (mustAdjustValue)
+        {
+        sprintf(label, format, val*scaleFactor);
+        }
+      else
+        {
+        sprintf(label, format, val);
+        }
+      if (fabs(val) < 0.01)
+        {
+        //
+        // Ensure that -0.0 is never a label
+        // The maximum number of digits that we allow past the decimal is 5.
+        //
+        if (strcmp(label, "-0") == 0)
+          {
+          sprintf(label, "0");
+          }
+        else if (strcmp(label, "-0.0") == 0)
+          {
+          sprintf(label, "0.0");
+          }
+        else if (strcmp(label, "-0.00") == 0)
+          {
+          sprintf(label, "0.00");
+          }
+        else if (strcmp(label, "-0.000") == 0)
+          {
+          sprintf(label, "0.000");
+          }
+        else if (strcmp(label, "-0.0000") == 0)
+          {
+          sprintf(label, "0.0000");
+          }
+        else if (strcmp(label, "-0.00000") == 0)
+          {
+          sprintf(label, "0.00000");
+          }
+        }
+      labels->SetValue(i, label);
+      val += deltaMajor;
+      }
+    }
+  else
+    {
+    if (labelCount > 0)
+      {
+      double delta = customizedLabels->GetNumberOfValues() / labelCount;
+      for (int i = 0; i < labelCount; ++i)
+        {
+        labels->SetValue(i, customizedLabels->GetValue(static_cast<vtkIdType>(i * delta)));
+        }
+      }
+    }
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    axes[i]->SetLabels(labels);
+    }
+  labels->Delete();
+}
+
+vtkStringArray* vtkRpCubeAxesActor::GetAxisLabels(int axis)
+{
+  return (axis >= 0 && axis < 3) ? this->AxisLabels[axis] : NULL;
+}
+
+void vtkRpCubeAxesActor::SetAxisLabels(int axis, vtkStringArray* value)
+{
+  if (axis >= 0 && axis < 3 && value != this->AxisLabels[axis])
+    {
+    vtkStringArray* previous = this->AxisLabels[axis];
+    if (value != NULL)
+      {
+      value->Register(this);
+      }
+    this->AxisLabels[axis] = value;
+    if (previous != NULL)
+      {
+      previous->UnRegister(this);
+      }
+    this->Modified();
+    }
+}
+
+// ****************************************************************************
+//  Set automatic label scaling mode, set exponents for each axis type.
+// ****************************************************************************
+void vtkRpCubeAxesActor::SetLabelScaling(bool autoscale, int upowX, int upowY,
+                                       int upowZ)
+{
+  if (autoscale != this->AutoLabelScaling || upowX != this->UserXPow ||
+      upowY != this->UserYPow || upowZ != this->UserZPow)
+    {
+    this->AutoLabelScaling = autoscale;
+    this->UserXPow = upowX;
+    this->UserYPow = upowY;
+    this->UserZPow = upowZ;
+    this->Modified();
+    }
+}
+
+// ****************************************************************************
+// Set the i-th title text property.
+// ****************************************************************************
+
+vtkTextProperty* vtkRpCubeAxesActor::GetTitleTextProperty(int axis)
+{
+  return (axis >= 0 && axis < 3) ? this->TitleTextProperty[axis] : NULL;
+}
+
+// ****************************************************************************
+// Get the i-th label text property.
+// ****************************************************************************
+
+vtkTextProperty* vtkRpCubeAxesActor::GetLabelTextProperty(int axis)
+{
+  return (axis >= 0 && axis < 3) ? this->LabelTextProperty[axis] : NULL;
+}
+
+// ****************************************************************************
+//  Set axes and screen size of the labels.
+// ****************************************************************************
+void vtkRpCubeAxesActor::UpdateLabels(vtkRpAxisActor **axis, int vtkNotUsed(index))
+  {
+  for (int i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+    {
+    int numberOfLabelsBuild = axis[i]->GetNumberOfLabelsBuilt();
+    vtkRpAxisFollower **labelActors = axis[i]->GetLabelActors();
+    for(int k=0; k < numberOfLabelsBuild; ++k)
+      {
+      labelActors[k]->SetEnableDistanceLOD( this->EnableDistanceLOD );
+      labelActors[k]->SetDistanceLODThreshold( this->DistanceLODThreshold );
+      labelActors[k]->SetEnableViewAngleLOD( this->EnableViewAngleLOD );
+      labelActors[k]->SetViewAngleLODThreshold( this->ViewAngleLODThreshold );
+      }
+    }
+  }
+// ****************************************************************************
+void vtkRpCubeAxesActor::SetXAxesLinesProperty(vtkProperty *prop)
+{
+  this->XAxesLinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetYAxesLinesProperty(vtkProperty *prop)
+{
+  this->YAxesLinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetZAxesLinesProperty(vtkProperty *prop)
+{
+  this->ZAxesLinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpCubeAxesActor::GetXAxesLinesProperty()
+{
+  return this->XAxesLinesProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetYAxesLinesProperty()
+{
+  return this->YAxesLinesProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetZAxesLinesProperty()
+{
+  return this->ZAxesLinesProperty;
+}
+
+// ****************************************************************************
+void vtkRpCubeAxesActor::SetXAxesGridlinesProperty(vtkProperty *prop)
+{
+  this->XAxesGridlinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetYAxesGridlinesProperty(vtkProperty *prop)
+{
+  this->YAxesGridlinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetZAxesGridlinesProperty(vtkProperty *prop)
+{
+  this->ZAxesGridlinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpCubeAxesActor::GetXAxesGridlinesProperty()
+{
+  return this->XAxesGridlinesProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetYAxesGridlinesProperty()
+{
+  return this->YAxesGridlinesProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetZAxesGridlinesProperty()
+{
+  return this->ZAxesGridlinesProperty;
+}
+
+// ****************************************************************************
+void vtkRpCubeAxesActor::SetXAxesInnerGridlinesProperty(vtkProperty *prop)
+{
+  this->XAxesInnerGridlinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetYAxesInnerGridlinesProperty(vtkProperty *prop)
+{
+  this->YAxesInnerGridlinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetZAxesInnerGridlinesProperty(vtkProperty *prop)
+{
+  this->ZAxesInnerGridlinesProperty->DeepCopy(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpCubeAxesActor::GetXAxesInnerGridlinesProperty()
+{
+  return this->XAxesInnerGridlinesProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetYAxesInnerGridlinesProperty()
+{
+  return this->YAxesInnerGridlinesProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetZAxesInnerGridlinesProperty()
+{
+  return this->ZAxesInnerGridlinesProperty;
+}
+
+// ****************************************************************************
+void vtkRpCubeAxesActor::SetXAxesGridpolysProperty(vtkProperty *prop)
+{
+  this->XAxesGridpolysProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetYAxesGridpolysProperty(vtkProperty *prop)
+{
+  this->YAxesGridpolysProperty->DeepCopy(prop);
+  this->Modified();
+}
+void vtkRpCubeAxesActor::SetZAxesGridpolysProperty(vtkProperty *prop)
+{
+  this->ZAxesGridpolysProperty->DeepCopy(prop);
+  this->Modified();
+}
+
+// ****************************************************************************
+vtkProperty* vtkRpCubeAxesActor::GetXAxesGridpolysProperty()
+{
+  return this->XAxesGridpolysProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetYAxesGridpolysProperty()
+{
+  return this->YAxesGridpolysProperty;
+}
+vtkProperty* vtkRpCubeAxesActor::GetZAxesGridpolysProperty()
+{
+  return this->ZAxesGridpolysProperty;
+}
+// --------------------------------------------------------------------------
+void vtkRpCubeAxesActor::UpdateGridLineVisibility(int idx)
+{
+  if( this->GridLineLocation != VTK_GRID_LINES_ALL &&
+      (this->DrawXGridlines || this->DrawYGridlines || this->DrawZGridlines) )
+    {
+    for(int i=0; i < NUMBER_OF_ALIGNED_AXIS; ++i)
+      {
+      this->XAxes[i]->SetDrawGridlines(0);
+      this->YAxes[i]->SetDrawGridlines(0);
+      this->ZAxes[i]->SetDrawGridlines(0);
+      this->XAxes[i]->SetDrawGridlinesOnly(0);
+      this->YAxes[i]->SetDrawGridlinesOnly(0);
+      this->ZAxes[i]->SetDrawGridlinesOnly(0);
+      }
+
+    this->XAxes[vtkRpCubeAxesActorTriads[idx][0]]->SetDrawGridlines(this->DrawXGridlines);
+    this->YAxes[vtkRpCubeAxesActorTriads[idx][1]]->SetDrawGridlines(this->DrawYGridlines);
+    this->ZAxes[vtkRpCubeAxesActorTriads[idx][2]]->SetDrawGridlines(this->DrawZGridlines);
+
+    // Update axis render list
+    int id = 0;
+    if(this->NumberOfAxesX == 1)
+      {
+      id = this->RenderAxesX[this->NumberOfAxesX] = vtkRpCubeAxesActorTriads[idx][0];
+      this->XAxes[id]->SetDrawGridlinesOnly((this->RenderAxesX[0] != id) ? 1 : 0);
+      this->NumberOfAxesX += (this->RenderAxesX[0] != id) ? 1 : 0;
+      }
+    if(this->NumberOfAxesY == 1)
+      {
+      id = this->RenderAxesY[this->NumberOfAxesY] = vtkRpCubeAxesActorTriads[idx][1];
+      this->YAxes[id]->SetDrawGridlinesOnly((this->RenderAxesY[0] != id) ? 1 : 0);
+      this->NumberOfAxesY += (this->RenderAxesY[0] != id) ? 1 : 0;
+      }
+    if(this->NumberOfAxesZ == 1)
+      {
+      id = this->RenderAxesZ[this->NumberOfAxesZ] = vtkRpCubeAxesActorTriads[idx][2];
+      this->ZAxes[id]->SetDrawGridlinesOnly((this->RenderAxesZ[0] != id) ? 1 : 0);
+      this->NumberOfAxesZ += (this->RenderAxesZ[0] != id) ? 1 : 0;
+      }
+    }
+}
+// --------------------------------------------------------------------------
+int vtkRpCubeAxesActor::FindClosestAxisIndex(double pts[8][3])
+{
+  // Loop over points and find the closest point to the camera
+  double min = VTK_FLOAT_MAX;
+  int idx = 0;
+  for (int i=0; i < 8; i++)
+    {
+    if (pts[i][2] < min)
+      {
+      idx = i;
+      min = pts[i][2];
+      }
+    }
+  return idx;
+}
+
+// --------------------------------------------------------------------------
+int vtkRpCubeAxesActor::FindFurtherstAxisIndex(double pts[8][3])
+{
+  // Loop over points and find the furthest point from the camera
+  double max = -VTK_FLOAT_MAX;
+  int idx = 0;
+  for (int i=0; i < 8; i++)
+    {
+    if (pts[i][2] > max)
+      {
+      idx = i;
+      max = pts[i][2];
+      }
+    }
+  return idx;
+}
+// --------------------------------------------------------------------------
+ void vtkRpCubeAxesActor::FindBoundaryEdge( int &xloc, int &yloc,
+                                          int &zloc, double pts[8][3])
+ {
+   // boundary edges fly mode
+   xloc = yloc = zloc = 1;
+   int i, xIdx = 0, yIdx = 0, zIdx = 0, zIdx2 = 0;
+   int xAxes = 0, yAxes = 0, zAxes = 0;
+   double slope = 0.0, minSlope, num, den, d2;
+   double e1[3], e2[3], e3[3];
+   int idx = 0;
+
+   // Find distance to origin
+   double d2Min = VTK_FLOAT_MAX;
+   for (i=0; i < 8; i++)
+     {
+     d2 = pts[i][0]*pts[i][0] + pts[i][1]*pts[i][1];
+     if (d2 < d2Min)
+       {
+       d2Min = d2;
+       idx = i;
+       }
+     }
+
+   // find minimum slope point connected to closest point and on
+   // right side (in projected coordinates). This is the first edge.
+   minSlope = VTK_FLOAT_MAX;
+   for (xIdx=0, i=0; i<3; i++)
+     {
+     num = (pts[vtkRpCubeAxesActorConn[idx][i]][1] - pts[idx][1]);
+     den = (pts[vtkRpCubeAxesActorConn[idx][i]][0] - pts[idx][0]);
+     if (den != 0.0)
+       {
+       slope = num / den;
+       }
+     if (slope < minSlope && den > 0)
+       {
+       xIdx = vtkRpCubeAxesActorConn[idx][i];
+       yIdx = vtkRpCubeAxesActorConn[idx][(i+1)%3];
+       zIdx = vtkRpCubeAxesActorConn[idx][(i+2)%3];
+       xAxes = i;
+       minSlope = slope;
+       }
+     }
+
+   // find edge (connected to closest point) on opposite side
+   for ( i=0; i<3; i++)
+     {
+     e1[i] = (pts[xIdx][i] - pts[idx][i]);
+     e2[i] = (pts[yIdx][i] - pts[idx][i]);
+     e3[i] = (pts[zIdx][i] - pts[idx][i]);
+     }
+   vtkMath::Normalize(e1);
+   vtkMath::Normalize(e2);
+   vtkMath::Normalize(e3);
+
+   if (vtkMath::Dot(e1,e2) < vtkMath::Dot(e1,e3))
+     {
+     yAxes = (xAxes + 1) % 3;
+     }
+   else
+     {
+     yIdx = zIdx;
+     yAxes = (xAxes + 2) % 3;
+     }
+
+   // Find the final point by determining which global x-y-z axes have not
+   // been represented, and then determine the point closest to the viewer.
+   zAxes = (xAxes != 0 && yAxes != 0 ? 0 :
+           (xAxes != 1 && yAxes != 1 ? 1 : 2));
+   if (pts[vtkRpCubeAxesActorConn[xIdx][zAxes]][2] <
+       pts[vtkRpCubeAxesActorConn[yIdx][zAxes]][2])
+     {
+     zIdx = xIdx;
+     zIdx2 = vtkRpCubeAxesActorConn[xIdx][zAxes];
+     }
+   else
+     {
+     zIdx = yIdx;
+     zIdx2 = vtkRpCubeAxesActorConn[yIdx][zAxes];
+     }
+
+   int mini = (idx < xIdx ? idx : xIdx);
+   switch (xAxes)
+     {
+     case 0:
+       xloc = vtkRpCubeAxesActorTriads[mini][0];
+       break;
+     case 1:
+       yloc = vtkRpCubeAxesActorTriads[mini][1];
+       break;
+     case 2:
+       zloc = vtkRpCubeAxesActorTriads[mini][2];
+       break;
+     }
+   mini = (idx < yIdx ? idx : yIdx);
+   switch (yAxes)
+     {
+     case 0:
+       xloc = vtkRpCubeAxesActorTriads[mini][0];
+       break;
+     case 1:
+       yloc =vtkRpCubeAxesActorTriads[mini][1];
+       break;
+     case 2:
+       zloc = vtkRpCubeAxesActorTriads[mini][2];
+       break;
+     }
+   mini = (zIdx < zIdx2 ? zIdx : zIdx2);
+   switch (zAxes)
+     {
+     case 0:
+       xloc = vtkRpCubeAxesActorTriads[mini][0];
+       break;
+     case 1:
+       yloc = vtkRpCubeAxesActorTriads[mini][1];
+       break;
+     case 2:
+       zloc = vtkRpCubeAxesActorTriads[mini][2];
+       break;
+     }
+ }
+
+// --------------------------------------------------------------------------
+int vtkRpCubeAxesActor::RenderGeometry(
+    bool &initialRender, vtkViewport *viewport, bool checkAxisVisibility,
+    int (vtkRpAxisActor::*renderMethod)(vtkViewport*))
+{
+  int i, renderedSomething = 0;
+
+  // Make sure axes are initialized and visibility is properly set
+  if(checkAxisVisibility)
+    {
+    // Initialization
+    if (!this->Camera)
+      {
+      vtkErrorMacro(<<"No camera!");
+      this->RenderSomething = 0;
+      return 0;
+      }
+
+    this->BuildAxes(viewport);
+
+    if (initialRender)
+      {
+      for (i = 0; i < NUMBER_OF_ALIGNED_AXIS; i++)
+        {
+        this->XAxes[i]->BuildAxis(viewport, true);
+        this->YAxes[i]->BuildAxis(viewport, true);
+        this->ZAxes[i]->BuildAxis(viewport, true);
+        }
+      }
+    initialRender = false;
+
+    this->DetermineRenderAxes(viewport);
+    }
+
+  // Render the axes
+  for (i = 0; i < this->NumberOfAxesX; i++)
+    {
+    renderedSomething +=
+        (this->XAxes[this->RenderAxesX[i]]->*renderMethod)(viewport);
+    }
+
+  for (i = 0; i < this->NumberOfAxesY; i++)
+    {
+    renderedSomething +=
+        (this->YAxes[this->RenderAxesY[i]]->*renderMethod)(viewport);
+    }
+
+  for (i = 0; i < this->NumberOfAxesZ; i++)
+    {
+    renderedSomething +=
+        (this->ZAxes[this->RenderAxesZ[i]]->*renderMethod)(viewport);
+    }
+  return renderedSomething;
+}
+
+// --------------------------------------------------------------------------
+void vtkRpCubeAxesActor::GetRenderedBounds(double *b)
+{
+  vtkBoundingBox bbox(this->GetBounds()); // Data bounds
+
+  // Make a heuristic on the final bounds that embed test labels
+  // Just inflate the box based on its max length
+  bbox.Inflate(bbox.GetMaxLength());
+
+  bbox.GetBounds(b);
+}
+
+// --------------------------------------------------------------------------
+double* vtkRpCubeAxesActor::GetRenderedBounds()
+{
+  this->GetRenderedBounds(this->RenderedBounds);
+  // Return our data holder
+  return this->RenderedBounds;
+}
Index: vtkvis/tags/1.8.4/vtkRpCubeAxesActor.h
===================================================================
--- vtkvis/tags/1.8.4/vtkRpCubeAxesActor.h	(revision 6184)
+++ vtkvis/tags/1.8.4/vtkRpCubeAxesActor.h	(revision 6184)
@@ -0,0 +1,711 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkRpCubeAxesActor.h
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+  Thanks:    Kathleen Bonnell, B Division, Lawrence Livermore Nat'l Laboratory
+
+Copyright (c) 1993-2001 Ken Martin, Will Schroeder, Bill Lorensen
+All rights reserve
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+=========================================================================*/
+// .NAME vtkRpCubeAxesActor - create a  plot of a bounding box edges -
+// used for navigation
+// .SECTION Description
+// vtkRpCubeAxesActor is a composite actor that draws axes of the
+// bounding box of an input dataset. The axes include labels and titles
+// for the x-y-z axes. The algorithm selects which axes to draw based
+// on the user-defined 'fly' mode.  (STATIC is default).
+// 'STATIC' constructs axes from all edges of the bounding box.
+// 'CLOSEST_TRIAD' consists of the three axes x-y-z forming a triad that
+// lies closest to the specified camera.
+// 'FURTHEST_TRIAD' consists of the three axes x-y-z forming a triad that
+// lies furthest from the specified camera.
+// 'OUTER_EDGES' is constructed from edges that are on the "exterior" of the
+// bounding box, exterior as determined from examining outer edges of the
+// bounding box in projection (display) space.
+//
+// To use this object you must define a bounding box and the camera used
+// to render the vtkRpCubeAxesActor. You can optionally turn on/off labels,
+// ticks, gridlines, and set tick location, number of labels, and text to
+// use for axis-titles.  A 'corner offset' can also be set.  This allows
+// the axes to be set partially away from the actual bounding box to perhaps
+// prevent overlap of labels between the various axes.
+//
+// The Bounds instance variable (an array of six doubles) is used to determine
+// the bounding box.
+//
+// .SECTION Thanks
+// This class was written by:
+// Hank Childs, Kathleen Bonnell, Amy Squillacote, Brad Whitlock, Will Schroeder,
+// Eric Brugger, Daniel Aguilera, Claire Guilbaud, Nicolas Dolegieviez,
+// Aashish Chaudhary, Philippe Pebay, David Gobbi, David Partyka, Utkarsh Ayachit
+// David Cole, Francois Bertel, and Mark Olesen
+// Part of this work was supported by CEA/DIF - Commissariat a l'Energie Atomique, 
+// Centre DAM Ile-De-France, BP12, F-91297 Arpajon, France.
+//
+// .section See Also
+// vtkActor vtkAxisActor vtkCubeAxesActor2D
+
+#ifndef vtkRpCubeAxesActor_h
+#define vtkRpCubeAxesActor_h
+
+#define VTK_FLY_OUTER_EDGES     0
+#define VTK_FLY_CLOSEST_TRIAD   1
+#define VTK_FLY_FURTHEST_TRIAD  2
+#define VTK_FLY_STATIC_TRIAD    3
+#define VTK_FLY_STATIC_EDGES    4
+
+#define VTK_TICKS_INSIDE        0
+#define VTK_TICKS_OUTSIDE       1
+#define VTK_TICKS_BOTH          2
+
+#define VTK_GRID_LINES_ALL      0
+#define VTK_GRID_LINES_CLOSEST  1
+#define VTK_GRID_LINES_FURTHEST 2
+
+#define NUMBER_OF_ALIGNED_AXIS 4
+
+#include "vtkRenderingAnnotationModule.h" // For export macro
+#include "vtkActor.h"
+
+class vtkRpAxisActor;
+class vtkCamera;
+class vtkTextProperty;
+class vtkStringArray;
+
+class VTKRENDERINGANNOTATION_EXPORT vtkRpCubeAxesActor : public vtkActor
+{
+public:
+  vtkTypeMacro(vtkRpCubeAxesActor,vtkActor);
+  void PrintSelf(ostream& os, vtkIndent indent);
+
+  // Description:
+  // Instantiate object with label format "6.3g" and the number of labels
+  // per axis set to 3.
+  static vtkRpCubeAxesActor *New();
+
+  // Description:
+  // Draw the axes as per the vtkProp superclass' API.
+  virtual int RenderOpaqueGeometry(vtkViewport*);
+  virtual int RenderTranslucentGeometry(vtkViewport*);
+  virtual int RenderTranslucentPolygonalGeometry(vtkViewport*);
+  virtual int RenderOverlay(vtkViewport*);
+  int HasTranslucentPolygonalGeometry();
+
+  // Description:
+  // Gets/Sets the RebuildAxes flag
+  vtkSetMacro( RebuildAxes, bool );
+  vtkGetMacro( RebuildAxes, bool );
+
+  // Description:
+  // Explicitly specify the region in space around which to draw the bounds.
+  // The bounds is used only when no Input or Prop is specified. The bounds
+  // are specified according to (xmin,xmax, ymin,ymax, zmin,zmax), making
+  // sure that the min's are less than the max's.
+  vtkSetVector6Macro(Bounds,double);
+  vtkGetVector6Macro(Bounds,double);
+
+  // Description:
+  // Method used to properly return the bounds of the cube axis itself with all
+  // its labels.
+  virtual void GetRenderedBounds(double rBounds[6]);
+  virtual double* GetRenderedBounds();
+
+  // Description:
+  // Explicitly specify the range of each axes that's used to define the prop.
+  // The default, (if you do not use these methods) is to use the bounds
+  // specified, or use the bounds of the Input Prop if one is specified. This
+  // method allows you to separate the notion of extent of the axes in physical
+  // space (bounds) and the extent of the values it represents. In other words,
+  // you can have the ticks and labels show a different range.
+  vtkSetVector2Macro( XAxisRange, double );
+  vtkSetVector2Macro( YAxisRange, double );
+  vtkSetVector2Macro( ZAxisRange, double );
+  vtkGetVector2Macro( XAxisRange, double );
+  vtkGetVector2Macro( YAxisRange, double );
+  // Description
+  // Explicitly specify the axis labels along an axis as an array of strings
+  // instead of using the values.
+  vtkStringArray* GetAxisLabels(int axis);
+  void SetAxisLabels(int axis, vtkStringArray* value);
+
+  vtkGetVector2Macro( ZAxisRange, double );
+
+  // Description:
+  // Explicitly specify the screen size of title and label text.
+  // ScreenSize determines the size of the text in terms of screen
+  // pixels. Default is 10.0.
+  void SetScreenSize(double screenSize);
+  vtkGetMacro(ScreenSize, double);
+
+  // Description:
+  // Explicitly specify the distance between labels and the axis.
+  // Default is 20.0.
+  void SetLabelOffset(double offset);
+  vtkGetMacro(LabelOffset, double);
+
+  // Description:
+  // Explicitly specify the distance between title and labels.
+  // Default is 20.0.
+  void SetTitleOffset(double offset);
+  vtkGetMacro(TitleOffset, double);
+
+  // Description:
+  // Set/Get the camera to perform scaling and translation of the
+  // vtkRpCubeAxesActor.
+  virtual void SetCamera(vtkCamera*);
+  vtkGetObjectMacro(Camera,vtkCamera);
+
+  // Description:
+  // Specify a mode to control how the axes are drawn: either static,
+  // closest triad, furthest triad or outer edges in relation to the
+  // camera position.
+  vtkSetClampMacro(FlyMode, int, VTK_FLY_OUTER_EDGES, VTK_FLY_STATIC_EDGES);
+  vtkGetMacro(FlyMode, int);
+  void SetFlyModeToOuterEdges()
+    {this->SetFlyMode(VTK_FLY_OUTER_EDGES);};
+  void SetFlyModeToClosestTriad()
+    {this->SetFlyMode(VTK_FLY_CLOSEST_TRIAD);};
+  void SetFlyModeToFurthestTriad()
+    {this->SetFlyMode(VTK_FLY_FURTHEST_TRIAD);};
+  void SetFlyModeToStaticTriad()
+    {this->SetFlyMode(VTK_FLY_STATIC_TRIAD);};
+  void SetFlyModeToStaticEdges()
+    {this->SetFlyMode(VTK_FLY_STATIC_EDGES);};
+
+  // Description:
+  // Set/Get the labels for the x, y, and z axes. By default,
+  // use "X-Axis", "Y-Axis" and "Z-Axis".
+  vtkSetStringMacro(XTitle);
+  vtkGetStringMacro(XTitle);
+  vtkSetStringMacro(XUnits);
+  vtkGetStringMacro(XUnits);
+  vtkSetStringMacro(YTitle);
+  vtkGetStringMacro(YTitle);
+  vtkSetStringMacro(YUnits);
+  vtkGetStringMacro(YUnits);
+  vtkSetStringMacro(ZTitle);
+  vtkGetStringMacro(ZTitle);
+  vtkSetStringMacro(ZUnits);
+  vtkGetStringMacro(ZUnits);
+
+  // Description:
+  // Set/Get the format with which to print the labels on each of the
+  // x-y-z axes.
+  vtkSetStringMacro(XLabelFormat);
+  vtkGetStringMacro(XLabelFormat);
+  vtkSetStringMacro(YLabelFormat);
+  vtkGetStringMacro(YLabelFormat);
+  vtkSetStringMacro(ZLabelFormat);
+  vtkGetStringMacro(ZLabelFormat);
+
+  // Description:
+  // Set/Get the inertial factor that controls how often (i.e, how
+  // many renders) the axes can switch position (jump from one axes
+  // to another).
+  vtkSetClampMacro(Inertia, int, 1, VTK_INT_MAX);
+  vtkGetMacro(Inertia, int);
+
+  // Description:
+  // Specify an offset value to "pull back" the axes from the corner at
+  // which they are joined to avoid overlap of axes labels. The
+  // "CornerOffset" is the fraction of the axis length to pull back.
+  vtkSetMacro(CornerOffset, double);
+  vtkGetMacro(CornerOffset, double);
+
+  // Description:
+  // Release any graphics resources that are being consumed by this actor.
+  // The parameter window could be used to determine which graphic
+  // resources to release.
+  void ReleaseGraphicsResources(vtkWindow *);
+
+  // Description:
+  // Enable and disable the use of distance based LOD for titles and labels.
+  vtkSetMacro( EnableDistanceLOD, int );
+  vtkGetMacro( EnableDistanceLOD, int );
+
+  // Description:a
+  // Set distance LOD threshold [0.0 - 1.0] for titles and labels.
+  vtkSetClampMacro( DistanceLODThreshold, double, 0.0, 1.0 );
+  vtkGetMacro( DistanceLODThreshold, double);
+
+  // Description:
+  // Enable and disable the use of view angle based LOD for titles and labels.
+  vtkSetMacro( EnableViewAngleLOD, int );
+  vtkGetMacro( EnableViewAngleLOD, int );
+
+  // Description:
+  // Set view angle LOD threshold [0.0 - 1.0] for titles and labels.
+  vtkSetClampMacro( ViewAngleLODThreshold, double, 0., 1. );
+  vtkGetMacro( ViewAngleLODThreshold, double );
+
+  // Description:
+  // Turn on and off the visibility of each axis.
+  vtkSetMacro(XAxisVisibility,int);
+  vtkGetMacro(XAxisVisibility,int);
+  vtkBooleanMacro(XAxisVisibility,int);
+  vtkSetMacro(YAxisVisibility,int);
+  vtkGetMacro(YAxisVisibility,int);
+  vtkBooleanMacro(YAxisVisibility,int);
+  vtkSetMacro(ZAxisVisibility,int);
+  vtkGetMacro(ZAxisVisibility,int);
+  vtkBooleanMacro(ZAxisVisibility,int);
+
+  // Description:
+  // Turn on and off the visibility of labels for each axis.
+  vtkSetMacro(XAxisLabelVisibility,int);
+  vtkGetMacro(XAxisLabelVisibility,int);
+  vtkBooleanMacro(XAxisLabelVisibility,int);
+
+  vtkSetMacro(YAxisLabelVisibility,int);
+  vtkGetMacro(YAxisLabelVisibility,int);
+  vtkBooleanMacro(YAxisLabelVisibility,int);
+
+  vtkSetMacro(ZAxisLabelVisibility,int);
+  vtkGetMacro(ZAxisLabelVisibility,int);
+  vtkBooleanMacro(ZAxisLabelVisibility,int);
+
+  // Description:
+  // Turn on and off the visibility of ticks for each axis.
+  vtkSetMacro(XAxisTickVisibility,int);
+  vtkGetMacro(XAxisTickVisibility,int);
+  vtkBooleanMacro(XAxisTickVisibility,int);
+
+  vtkSetMacro(YAxisTickVisibility,int);
+  vtkGetMacro(YAxisTickVisibility,int);
+  vtkBooleanMacro(YAxisTickVisibility,int);
+
+  vtkSetMacro(ZAxisTickVisibility,int);
+  vtkGetMacro(ZAxisTickVisibility,int);
+  vtkBooleanMacro(ZAxisTickVisibility,int);
+
+  // Description:
+  // Turn on and off the visibility of minor ticks for each axis.
+  vtkSetMacro(XAxisMinorTickVisibility,int);
+  vtkGetMacro(XAxisMinorTickVisibility,int);
+  vtkBooleanMacro(XAxisMinorTickVisibility,int);
+
+  vtkSetMacro(YAxisMinorTickVisibility,int);
+  vtkGetMacro(YAxisMinorTickVisibility,int);
+  vtkBooleanMacro(YAxisMinorTickVisibility,int);
+
+  vtkSetMacro(ZAxisMinorTickVisibility,int);
+  vtkGetMacro(ZAxisMinorTickVisibility,int);
+  vtkBooleanMacro(ZAxisMinorTickVisibility,int);
+
+  vtkSetMacro(DrawXGridlines,int);
+  vtkGetMacro(DrawXGridlines,int);
+  vtkBooleanMacro(DrawXGridlines,int);
+
+  vtkSetMacro(DrawYGridlines,int);
+  vtkGetMacro(DrawYGridlines,int);
+  vtkBooleanMacro(DrawYGridlines,int);
+
+  vtkSetMacro(DrawZGridlines,int);
+  vtkGetMacro(DrawZGridlines,int);
+  vtkBooleanMacro(DrawZGridlines,int);
+
+  vtkSetMacro(DrawXInnerGridlines,int);
+  vtkGetMacro(DrawXInnerGridlines,int);
+  vtkBooleanMacro(DrawXInnerGridlines,int);
+
+  vtkSetMacro(DrawYInnerGridlines,int);
+  vtkGetMacro(DrawYInnerGridlines,int);
+  vtkBooleanMacro(DrawYInnerGridlines,int);
+
+  vtkSetMacro(DrawZInnerGridlines,int);
+  vtkGetMacro(DrawZInnerGridlines,int);
+  vtkBooleanMacro(DrawZInnerGridlines,int);
+
+  vtkSetMacro(DrawXGridpolys,int);
+  vtkGetMacro(DrawXGridpolys,int);
+  vtkBooleanMacro(DrawXGridpolys,int);
+
+  vtkSetMacro(DrawYGridpolys,int);
+  vtkGetMacro(DrawYGridpolys,int);
+  vtkBooleanMacro(DrawYGridpolys,int);
+
+  vtkSetMacro(DrawZGridpolys,int);
+  vtkGetMacro(DrawZGridpolys,int);
+  vtkBooleanMacro(DrawZGridpolys,int);
+
+  // Description:
+  // Returns the text property for the title on an axis.
+  vtkTextProperty *GetTitleTextProperty(int);
+
+  // Description:
+  // Returns the text property for the labels on an axis.
+  vtkTextProperty *GetLabelTextProperty(int);
+
+  // Description:
+  // Get/Set axes actors properties.
+  void SetXAxesLinesProperty(vtkProperty *);
+  vtkProperty* GetXAxesLinesProperty();
+  void SetYAxesLinesProperty(vtkProperty *);
+  vtkProperty* GetYAxesLinesProperty();
+  void SetZAxesLinesProperty(vtkProperty *);
+  vtkProperty* GetZAxesLinesProperty();
+
+  // Description:
+  // Get/Set axes (outer) gridlines actors properties.
+  void SetXAxesGridlinesProperty(vtkProperty *);
+  vtkProperty* GetXAxesGridlinesProperty();
+  void SetYAxesGridlinesProperty(vtkProperty *);
+  vtkProperty* GetYAxesGridlinesProperty();
+  void SetZAxesGridlinesProperty(vtkProperty *);
+  vtkProperty* GetZAxesGridlinesProperty();
+
+  // Description:
+  // Get/Set axes inner gridlines actors properties.
+  void SetXAxesInnerGridlinesProperty(vtkProperty *);
+  vtkProperty* GetXAxesInnerGridlinesProperty();
+  void SetYAxesInnerGridlinesProperty(vtkProperty *);
+  vtkProperty* GetYAxesInnerGridlinesProperty();
+  void SetZAxesInnerGridlinesProperty(vtkProperty *);
+  vtkProperty* GetZAxesInnerGridlinesProperty();
+
+  // Description:
+  // Get/Set axes gridPolys actors properties.
+  void SetXAxesGridpolysProperty(vtkProperty *);
+  vtkProperty* GetXAxesGridpolysProperty();
+  void SetYAxesGridpolysProperty(vtkProperty *);
+  vtkProperty* GetYAxesGridpolysProperty();
+  void SetZAxesGridpolysProperty(vtkProperty *);
+  vtkProperty* GetZAxesGridpolysProperty();
+
+  // Description:
+  // Set/Get the location of ticks marks.
+  vtkSetClampMacro(TickLocation, int, VTK_TICKS_INSIDE, VTK_TICKS_BOTH);
+  vtkGetMacro(TickLocation, int);
+
+  void SetTickLocationToInside(void)
+    { this->SetTickLocation(VTK_TICKS_INSIDE); };
+  void SetTickLocationToOutside(void)
+    { this->SetTickLocation(VTK_TICKS_OUTSIDE); };
+  void SetTickLocationToBoth(void)
+    { this->SetTickLocation(VTK_TICKS_BOTH); };
+
+  void SetLabelScaling(bool, int, int, int);
+
+  // Description:
+  // Get/Set 2D mode
+  // NB: Use vtkTextActor for titles in 2D instead of vtkAxisFollower
+  void SetUse2DMode( int val );
+  int GetUse2DMode();
+
+  // Description:
+  // For 2D mode only: save axis title positions for later use
+  void SetSaveTitlePosition( int val );
+
+  // Description:
+  // Provide an oriented bounded box when using AxisBaseFor.
+  vtkSetVector6Macro(OrientedBounds,double);
+  vtkGetVector6Macro(OrientedBounds, double);
+
+  // Description:
+  // Enable/Disable the usage of the OrientedBounds
+  vtkSetMacro(UseOrientedBounds, int);
+  vtkGetMacro(UseOrientedBounds, int);
+
+  // Description:
+  // Vector that should be use as the base for X
+  vtkSetVector3Macro(AxisBaseForX,double);
+  vtkGetVector3Macro(AxisBaseForX, double);
+
+  // Description:
+  // Vector that should be use as the base for Y
+  vtkSetVector3Macro(AxisBaseForY,double);
+  vtkGetVector3Macro(AxisBaseForY, double);
+
+  // Description:
+  // Vector that should be use as the base for Z
+  vtkSetVector3Macro(AxisBaseForZ,double);
+  vtkGetVector3Macro(AxisBaseForZ, double);
+
+  // Description:
+  // Provide a custom AxisOrigin. This point must be inside the bouding box and
+  // will represent the point where the 3 axes will interesect
+  vtkSetVector3Macro(AxisOrigin,double);
+  vtkGetVector3Macro(AxisOrigin, double);
+
+  // Description:
+  // Enable/Disable the usage of the AxisOrigin
+  vtkSetMacro(UseAxisOrigin, int);
+  vtkGetMacro(UseAxisOrigin, int);
+
+  // Description:
+  // Specify the mode in which the cube axes should render its gridLines
+  vtkSetMacro(GridLineLocation,int);
+  vtkGetMacro(GridLineLocation,int);
+
+  vtkSetMacro(XAutoLabelFormat,int);
+  vtkGetMacro(XAutoLabelFormat,int);
+  vtkBooleanMacro(XAutoLabelFormat,int);
+  vtkSetMacro(YAutoLabelFormat,int);
+  vtkGetMacro(YAutoLabelFormat,int);
+  vtkBooleanMacro(YAutoLabelFormat,int);
+  vtkSetMacro(ZAutoLabelFormat,int);
+  vtkGetMacro(ZAutoLabelFormat,int);
+  vtkBooleanMacro(ZAutoLabelFormat,int);
+
+protected:
+  vtkRpCubeAxesActor();
+  ~vtkRpCubeAxesActor();
+
+  int LabelExponent(double min, double max);
+
+  int Digits(double min, double max);
+
+  double MaxOf(double, double);
+  double MaxOf(double, double, double, double);
+
+  double FFix(double);
+  double FSign(double, double);
+  int FRound( double fnt );
+  int GetNumTicks( double range, double fxt);
+
+  void UpdateLabels(vtkRpAxisActor **axis, int index);
+
+  double Bounds[6]; //Define bounds explicitly
+
+  vtkCamera *Camera;
+
+  int FlyMode;
+
+  // Expose internally closest axis index computation
+  int FindClosestAxisIndex(double pts[8][3]);
+
+  // Expose internally furthest axis index computation
+  int FindFurtherstAxisIndex(double pts[8][3]);
+
+  // Expose internally the boundary edge fly mode axis index computation
+  void FindBoundaryEdge(int &indexOfAxisX, int &indexOfAxisY, int &indexOfAxisZ,
+                        double pts[8][3]);
+
+  // Description:
+  // This will Update AxisActors with GridVisibility when those should be
+  // dynamaic regarding the viewport.
+  // GridLineLocation = [VTK_CLOSEST_GRID_LINES, VTK_FURTHEST_GRID_LINES]
+  void UpdateGridLineVisibility(int axisIndex);
+
+  // VTK_ALL_GRID_LINES      0
+  // VTK_CLOSEST_GRID_LINES  1
+  // VTK_FURTHEST_GRID_LINES 2
+  int GridLineLocation;
+
+  // Description:
+  // If enabled the actor will not be visible at a certain distance from the camera.
+  // Default is true
+  int EnableDistanceLOD;
+
+  // Description:
+  // Default is 0.80
+  // This determines at what fraction of camera far clip range, actor is not visible.
+  double DistanceLODThreshold;
+
+  // Description:
+  // If enabled the actor will not be visible at a certain view angle.
+  // Default is true.
+  int EnableViewAngleLOD;
+
+  // Description:
+  // This determines at what view angle to geometry will make the geometry not visibile.
+  // Default is 0.3.
+  double ViewAngleLODThreshold;
+
+  // Description:
+  // Control variables for all axes
+  // NB: [0] always for 'Major' axis during non-static fly modes.
+  vtkRpAxisActor *XAxes[NUMBER_OF_ALIGNED_AXIS];
+  vtkRpAxisActor *YAxes[NUMBER_OF_ALIGNED_AXIS];
+  vtkRpAxisActor *ZAxes[NUMBER_OF_ALIGNED_AXIS];
+
+  bool RebuildAxes;
+
+  char *XTitle;
+  char *XUnits;
+  char *YTitle;
+  char *YUnits;
+  char *ZTitle;
+  char *ZUnits;
+
+  char *ActualXLabel;
+  char *ActualYLabel;
+  char *ActualZLabel;
+
+  int TickLocation;
+
+  int XAxisVisibility;
+  int YAxisVisibility;
+  int ZAxisVisibility;
+
+  int XAxisTickVisibility;
+  int YAxisTickVisibility;
+  int ZAxisTickVisibility;
+
+  int XAxisMinorTickVisibility;
+  int YAxisMinorTickVisibility;
+  int ZAxisMinorTickVisibility;
+
+  int XAxisLabelVisibility;
+  int YAxisLabelVisibility;
+  int ZAxisLabelVisibility;
+
+  int DrawXGridlines;
+  int DrawYGridlines;
+  int DrawZGridlines;
+
+  int DrawXInnerGridlines;
+  int DrawYInnerGridlines;
+  int DrawZInnerGridlines;
+
+  int DrawXGridpolys;
+  int DrawYGridpolys;
+  int DrawZGridpolys;
+
+  char  *XLabelFormat;
+  char  *YLabelFormat;
+  char  *ZLabelFormat;
+
+  double CornerOffset;
+
+  int   Inertia;
+
+  int   RenderCount;
+
+  int   InertiaLocs[3];
+
+  int RenderSomething;
+
+  vtkTextProperty* TitleTextProperty[3];
+  vtkStringArray* AxisLabels[3];
+
+  vtkTextProperty* LabelTextProperty[3];
+
+  vtkProperty  *XAxesLinesProperty;
+  vtkProperty  *YAxesLinesProperty;
+  vtkProperty  *ZAxesLinesProperty;
+  vtkProperty  *XAxesGridlinesProperty;
+  vtkProperty  *YAxesGridlinesProperty;
+  vtkProperty  *ZAxesGridlinesProperty;
+  vtkProperty  *XAxesInnerGridlinesProperty;
+  vtkProperty  *YAxesInnerGridlinesProperty;
+  vtkProperty  *ZAxesInnerGridlinesProperty;
+  vtkProperty  *XAxesGridpolysProperty;
+  vtkProperty  *YAxesGridpolysProperty;
+  vtkProperty  *ZAxesGridpolysProperty;
+
+  double RenderedBounds[6];
+  double OrientedBounds[6];
+  int UseOrientedBounds;
+
+  double AxisOrigin[3];
+  int UseAxisOrigin;
+
+  double AxisBaseForX[3];
+  double AxisBaseForY[3];
+  double AxisBaseForZ[3];
+
+private:
+  vtkRpCubeAxesActor(const vtkRpCubeAxesActor&); // Not implemented
+  void operator=(const vtkRpCubeAxesActor&); // Not implemented
+
+  vtkSetStringMacro(ActualXLabel);
+  vtkSetStringMacro(ActualYLabel);
+  vtkSetStringMacro(ActualZLabel);
+
+  vtkTimeStamp BuildTime;
+  int LastUseOrientedBounds;
+  int LastXPow;
+  int LastYPow;
+  int LastZPow;
+
+  int UserXPow;
+  int UserYPow;
+  int UserZPow;
+
+  bool AutoLabelScaling;
+  int XAutoLabelFormat;
+  int YAutoLabelFormat;
+  int ZAutoLabelFormat;
+
+  int LastXAxisDigits;
+  int LastYAxisDigits;
+  int LastZAxisDigits;
+
+  double LastXRange[2];
+  double LastYRange[2];
+  double LastZRange[2];
+  double LastBounds[6];
+
+  int    LastFlyMode;
+
+  int   RenderAxesX[NUMBER_OF_ALIGNED_AXIS];
+  int   RenderAxesY[NUMBER_OF_ALIGNED_AXIS];
+  int   RenderAxesZ[NUMBER_OF_ALIGNED_AXIS];
+
+  int   NumberOfAxesX;
+  int   NumberOfAxesY;
+  int   NumberOfAxesZ;
+
+  bool MustAdjustXValue;
+  bool MustAdjustYValue;
+  bool MustAdjustZValue;
+
+  bool ForceXLabelReset;
+  bool ForceYLabelReset;
+  bool ForceZLabelReset;
+
+  double XAxisRange[2];
+  double YAxisRange[2];
+  double ZAxisRange[2];
+
+  double LabelScale;
+  double TitleScale;
+
+  double ScreenSize;
+  double LabelOffset;
+  double TitleOffset;
+
+  // Description:
+  // Major start and delta values, in each direction.
+  // These values are needed for inner grid lines generation
+  double MajorStart[3];
+  double DeltaMajor[3];
+
+  int RenderGeometry(bool &initialRender, vtkViewport *viewport, bool checkAxisVisibility,int (vtkRpAxisActor::*renderMethod)(vtkViewport*));
+
+  void  TransformBounds(vtkViewport *viewport, const double bounds[6],
+                        double pts[8][3]);
+  void  AdjustAxes(double bounds[6],
+                   double xCoords[NUMBER_OF_ALIGNED_AXIS][6],
+                   double yCoords[NUMBER_OF_ALIGNED_AXIS][6],
+                   double zCoords[NUMBER_OF_ALIGNED_AXIS][6],
+                   double xRange[2], double yRange[2], double zRange[2]);
+
+  bool  ComputeTickSize(double bounds[6]);
+  void  AdjustValues(const double xRange[2],
+                     const double yRange[2],
+                     const double zRange[2]);
+  void  AdjustRange(const double bounds[6]);
+  void  BuildAxes(vtkViewport *);
+  void  DetermineRenderAxes(vtkViewport *);
+  void  SetNonDependentAttributes(void);
+  void  BuildLabels(vtkRpAxisActor *axes[NUMBER_OF_ALIGNED_AXIS]);
+  void  AdjustTicksComputeRange(vtkRpAxisActor *axes[NUMBER_OF_ALIGNED_AXIS],
+      double rangeMin, double rangeMax);
+
+  void    AutoScale(vtkViewport *viewport);
+  void    AutoScale(vtkViewport *viewport, vtkRpAxisActor *axes[NUMBER_OF_ALIGNED_AXIS]);
+  double  AutoScale(vtkViewport *viewport, double screenSize, double position[3]);
+};
+
+
+#endif
