Index: /trunk/packages/vizservers/vtkvis/Makefile.in
===================================================================
--- /trunk/packages/vizservers/vtkvis/Makefile.in	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/Makefile.in	(revision 2423)
@@ -47,5 +47,5 @@
 DEBUG			= #yes
 TRACE			= #yes
-USE_CUSTOM_AXES 	= #yes
+USE_CUSTOM_AXES 	= yes
 USE_GPU_RAYCASTING      = yes
 USE_OFFSCREEN_RENDERING = yes
Index: /trunk/packages/vizservers/vtkvis/RpContour2D.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpContour2D.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpContour2D.cpp	(revision 2423)
@@ -13,7 +13,9 @@
 #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>
@@ -107,6 +109,28 @@
             pd->GetNumberOfStrips() == 0) {
             // DataSet is a point cloud
-            if (_dataSet->is2D()) {
+            DataSet::PrincipalPlane plane;
+            double offset;
+            if (_dataSet->is2D(&plane, &offset)) {
                 vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                if (plane == DataSet::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 == DataSet::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->SetInput(pd);
                 _contourFilter->SetInputConnection(mesher->GetOutputPort());
@@ -154,5 +178,7 @@
         _contourMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
         _contourMapper->SetResolveCoincidentTopologyToPolygonOffset();
-        _contourMapper->SetInputConnection(_contourFilter->GetOutputPort());
+        vtkSmartPointer<vtkStripper> stripper = vtkSmartPointer<vtkStripper>::New();
+        stripper->SetInputConnection(_contourFilter->GetOutputPort());
+        _contourMapper->SetInputConnection(stripper->GetOutputPort());
         getActor()->SetMapper(_contourMapper);
     }
Index: /trunk/packages/vizservers/vtkvis/RpGlyphs.h
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpGlyphs.h	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpGlyphs.h	(revision 2423)
@@ -27,6 +27,5 @@
  * \brief Oriented and scaled 3D glyph shapes
  *
- * The DataSet must be a PolyData point set
- * with vectors and/or scalars
+ * The DataSet must have vectors and/or scalars
  */
 class Glyphs : public VtkGraphicsObject {
Index: /trunk/packages/vizservers/vtkvis/RpHeightMap.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpHeightMap.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpHeightMap.cpp	(revision 2423)
@@ -18,4 +18,5 @@
 #include <vtkImageData.h>
 #include <vtkLookupTable.h>
+#include <vtkTransform.h>
 #include <vtkDelaunay2D.h>
 #include <vtkDelaunay3D.h>
@@ -25,4 +26,5 @@
 #include <vtkDataSetSurfaceFilter.h>
 #include <vtkContourFilter.h>
+#include <vtkStripper.h>
 #include <vtkWarpScalar.h>
 #include <vtkPropAssembly.h>
@@ -35,10 +37,10 @@
 #define MESH_POINT_CLOUDS
 
-HeightMap::HeightMap(int numContours) :
+HeightMap::HeightMap(int numContours, double heightScale) :
     VtkGraphicsObject(),
     _numContours(numContours),
     _colorMap(NULL),
     _contourEdgeWidth(1.0),
-    _warpScale(1.0),
+    _warpScale(heightScale),
     _sliceAxis(Z_AXIS),
     _pipelineInitialized(false)
@@ -49,5 +51,5 @@
 }
 
-HeightMap::HeightMap(const std::vector<double>& contours) :
+HeightMap::HeightMap(const std::vector<double>& contours, double heightScale) :
     VtkGraphicsObject(),
     _numContours(contours.size()),
@@ -55,5 +57,5 @@
     _colorMap(NULL),
     _contourEdgeWidth(1.0),
-    _warpScale(1.0),
+    _warpScale(heightScale),
     _sliceAxis(Z_AXIS),
     _pipelineInitialized(false)
@@ -194,8 +196,32 @@
                 pd->GetNumberOfStrips() == 0) {
                 // DataSet is a point cloud
-                if (_dataSet->is2D()) {
+                DataSet::PrincipalPlane plane;
+                double offset;
+                if (_dataSet->is2D(&plane, &offset)) {
 #ifdef MESH_POINT_CLOUDS
                     // Result of Delaunay2D is a PolyData
                     vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                    if (plane == DataSet::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 == DataSet::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->SetInput(pd);
                     vtkAlgorithmOutput *warpOutput = initWarp(mesher->GetOutputPort());
@@ -205,9 +231,22 @@
                     if (_pointSplatter == NULL)
                         _pointSplatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                    if (_volumeSlicer == NULL)
+                        _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
                     _pointSplatter->SetInput(pd);
                     int dims[3];
                     _pointSplatter->GetSampleDimensions(dims);
                     TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
-                    dims[2] = 3;
+                    if (plane == DataSet::PLANE_ZY) {
+                        dims[0] = 3;
+                        _volumeSlicer->SetVOI(1, 1, 0, dims[1]-1, 0, dims[1]-1);
+                        _sliceAxis = X_AXIS;
+                    } else if (plane == DataSet::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);
+                    }
                     _pointSplatter->SetSampleDimensions(dims);
                     double bounds[6];
@@ -218,8 +257,5 @@
                           bounds[2], bounds[3],
                           bounds[4], bounds[5]);
-                    if (_volumeSlicer == NULL)
-                        _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
                     _volumeSlicer->SetInputConnection(_pointSplatter->GetOutputPort());
-                    _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, 1, 1);
                     _volumeSlicer->SetSampleRate(1, 1, 1);
                     vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
@@ -324,12 +360,37 @@
                 _probeFilter->SetSource(ds);
                 vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
+                DataSet::PrincipalPlane plane;
+                double offset;
                 int xdim = 256;
                 int ydim = 256;
                 int zdim = 1;
+                double origin[3];
+                double spacing[3];
+                origin[0] = bounds[0];
+                origin[1] = bounds[2];
+                origin[2] = bounds[4] + (bounds[5]-bounds[4])/2.;
+                spacing[0] = (bounds[1]-bounds[0])/((double)(xdim-1));
+                spacing[1] = (bounds[3]-bounds[2])/((double)(ydim-1));
+                spacing[2] = 0;
+                if (_dataSet->is2D(&plane, &offset)) {
+                    if (plane == DataSet::PLANE_ZY) {
+                        xdim = 1;
+                        zdim = 256;
+                        origin[2] = bounds[4];
+                        spacing[0] = 0;
+                        spacing[2] = (bounds[5]-bounds[4])/((double)(zdim-1));
+                        _sliceAxis = X_AXIS;
+                    } else if (plane == DataSet::PLANE_XZ) {
+                        ydim = 1;
+                        zdim = 256;
+                        origin[2] = bounds[4];
+                        spacing[1] = 0;
+                        spacing[2] = (bounds[5]-bounds[4])/((double)(zdim-1));
+                        _sliceAxis = Y_AXIS;
+                    }
+                }
                 imageData->SetDimensions(xdim, ydim, zdim);
-                imageData->SetOrigin(bounds[0], bounds[2], bounds[4] + (bounds[5]-bounds[4])/2.);
-                imageData->SetSpacing((bounds[1]-bounds[0])/((double)(xdim-1)), 
-                                      (bounds[3]-bounds[2])/((double)(ydim-1)),
-                                      0);
+                imageData->SetOrigin(origin);
+                imageData->SetSpacing(spacing);
                 _probeFilter->SetInput(imageData);
                 gf->SetInputConnection(_probeFilter->GetOutputPort());
@@ -377,5 +438,7 @@
         _contourMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
         _contourMapper->SetResolveCoincidentTopologyToPolygonOffset();
-        _contourMapper->SetInputConnection(_contourFilter->GetOutputPort());
+        vtkSmartPointer<vtkStripper> stripper = vtkSmartPointer<vtkStripper>::New();
+        stripper->SetInputConnection(_contourFilter->GetOutputPort());
+        _contourMapper->SetInputConnection(stripper->GetOutputPort());
         _contourActor->SetMapper(_contourMapper);
     }
@@ -582,16 +645,16 @@
             break;
         case Y_AXIS:
-            voi[2] = voi[3] = (int)((dims[1]-1) * ratio);
             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[4] = voi[5] = (int)((dims[2]-1) * ratio);
             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:
@@ -666,5 +729,5 @@
  * Will override any existing contours
  */
-void HeightMap::setContours(int numContours)
+void HeightMap::setNumContours(int numContours)
 {
     _contours.clear();
@@ -724,7 +787,17 @@
 
 /**
+ * \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::setContourVisibility(bool state)
+void HeightMap::setContourLineVisibility(bool state)
 {
     if (_contourActor != NULL) {
Index: /trunk/packages/vizservers/vtkvis/RpHeightMap.h
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpHeightMap.h	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpHeightMap.h	(revision 2423)
@@ -42,7 +42,7 @@
     };
 
-    HeightMap(int numContours);
+    HeightMap(int numContours, double heightScale = 1.0);
 
-    HeightMap(const std::vector<double>& contours);
+    HeightMap(const std::vector<double>& contours, double heightScale = 1.0);
 
     virtual ~HeightMap();
@@ -73,7 +73,5 @@
     void setHeightScale(double scale);
 
-    void setContours(int numContours);
-
-    void setContours(int numContours, double range[2]);
+    void setNumContours(int numContours);
 
     void setContourList(const std::vector<double>& contours);
@@ -100,5 +98,7 @@
                               double vectorComponentRange[3][2]);
 
-    void setContourVisibility(bool state);
+    void setContourLineVisibility(bool state);
+
+    void setContourSurfaceVisibility(bool state);
 
     void setContourEdgeColor(float color[3]);
Index: /trunk/packages/vizservers/vtkvis/RpPolyData.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpPolyData.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpPolyData.cpp	(revision 2423)
@@ -14,4 +14,5 @@
 #include <vtkActor.h>
 #include <vtkProperty.h>
+#include <vtkTransform.h>
 #include <vtkDelaunay2D.h>
 #include <vtkDelaunay3D.h>
@@ -78,9 +79,10 @@
     vtkPolyData *pd = vtkPolyData::SafeDownCast(ds);
     if (pd) {
-        TRACE("Verts: %d Lines: %d Polys: %d Strips: %d",
-                  pd->GetNumberOfVerts(),
-                  pd->GetNumberOfLines(),
-                  pd->GetNumberOfPolys(),
-                  pd->GetNumberOfStrips());
+        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 &&
@@ -88,10 +90,33 @@
             pd->GetNumberOfStrips() == 0) {
             // DataSet is a point cloud
-            if (_dataSet->is2D()) {
+            DataSet::PrincipalPlane plane;
+            double offset;
+            if (_dataSet->numDimensions() < 2 || pd->GetNumberOfPoints() < 3) {
+                // 0D or 1D or not enough points to mesh
+                _pdMapper->SetInput(pd);
+            } else if (_dataSet->is2D(&plane, &offset)) {
                 vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                if (plane == DataSet::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 == DataSet::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->SetInput(pd);
                 mesher->ReleaseDataFlagOn();
-                _pdMapper->SetInputConnection(mesher->GetOutputPort());
-#if defined(DEBUG) && defined(WANT_TRACE)
                 mesher->Update();
                 vtkPolyData *outpd = mesher->GetOutput();
@@ -101,17 +126,32 @@
                       outpd->GetNumberOfPolys(),
                       outpd->GetNumberOfStrips());
-#endif
+                if (outpd->GetNumberOfPolys() == 0) {
+                    WARN("Delaunay2D mesher failed");
+                    _pdMapper->SetInput(pd);
+                } else {
+                    _pdMapper->SetInputConnection(mesher->GetOutputPort());
+                }
             } else {
                 vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
                 mesher->SetInput(pd);
                 mesher->ReleaseDataFlagOn();
-                // 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->SetInputConnection(mesher->GetOutputPort());
-                gf->ReleaseDataFlagOn();
-                _pdMapper->SetInputConnection(gf->GetOutputPort());
-            }
+                mesher->Update();
+                vtkUnstructuredGrid *grid = mesher->GetOutput();
+                TRACE("Delaunay3D Cells: %d",
+                      grid->GetNumberOfCells());
+                if (grid->GetNumberOfCells() == 0) {
+                    WARN("Delaunay3D mesher failed");
+                    _pdMapper->SetInput(pd);
+                } 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->SetInputConnection(mesher->GetOutputPort());
+                    gf->ReleaseDataFlagOn();
+                    _pdMapper->SetInputConnection(gf->GetOutputPort());
+                }
+             }
         } else {
             // DataSet is a vtkPolyData with lines and/or polygons
Index: /trunk/packages/vizservers/vtkvis/RpPolyData.h
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpPolyData.h	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpPolyData.h	(revision 2423)
@@ -22,5 +22,5 @@
  * \brief VTK Mesh (Polygon data)
  *
- * The DataSet must be a PolyData object
+ * This class creates a boundary mesh of a DataSet
  */
 class PolyData : public VtkGraphicsObject {
Index: /trunk/packages/vizservers/vtkvis/RpPseudoColor.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpPseudoColor.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpPseudoColor.cpp	(revision 2423)
@@ -15,4 +15,5 @@
 #include <vtkImageData.h>
 #include <vtkLookupTable.h>
+#include <vtkTransform.h>
 #include <vtkDelaunay2D.h>
 #include <vtkDelaunay3D.h>
@@ -84,18 +85,48 @@
             pd->GetNumberOfStrips() == 0) {
             // DataSet is a point cloud
-            if (_dataSet->is2D()) {
+            DataSet::PrincipalPlane plane;
+            double offset;
+            if (_dataSet->is2D(&plane, &offset)) {
 #ifdef MESH_POINT_CLOUDS
                 vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
+                if (plane == DataSet::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 == DataSet::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->SetInput(pd);
-                vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
-                gf->SetInputConnection(mesher->GetOutputPort());
-                _dsMapper->SetInputConnection(gf->GetOutputPort());
+                _dsMapper->SetInputConnection(mesher->GetOutputPort());
 #else
                 vtkSmartPointer<vtkGaussianSplatter> splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
+                vtkSmartPointer<vtkExtractVOI> slicer = vtkSmartPointer<vtkExtractVOI>::New();
                 splatter->SetInput(pd);
                 int dims[3];
                 splatter->GetSampleDimensions(dims);
                 TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
-                dims[2] = 3;
+                if (plane == DataSet::PLANE_ZY) {
+                    dims[0] = 3;
+                    slicer->SetVOI(1, 1, 0, dims[1]-1, 0, dims[1]-1);
+                } else if (plane == DataSet::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];
@@ -106,7 +137,5 @@
                       bounds[2], bounds[3],
                       bounds[4], bounds[5]);
-                vtkSmartPointer<vtkExtractVOI> slicer = vtkSmartPointer<vtkExtractVOI>::New();
                 slicer->SetInputConnection(splatter->GetOutputPort());
-                slicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, 1, 1);
                 slicer->SetSampleRate(1, 1, 1);
                 vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
Index: /trunk/packages/vizservers/vtkvis/RpPseudoColor.h
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpPseudoColor.h	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpPseudoColor.h	(revision 2423)
@@ -23,6 +23,4 @@
 /**
  * \brief Color-mapped plot of data set
- * 
- * Currently the DataSet must be image data (2D uniform grid)
  */
 class PseudoColor : public VtkGraphicsObject {
Index: /trunk/packages/vizservers/vtkvis/RpVtkDataSet.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpVtkDataSet.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpVtkDataSet.cpp	(revision 2423)
@@ -6,4 +6,5 @@
  */
 
+#include <cassert>
 #include <cstring>
 #include <cfloat>
@@ -162,4 +163,52 @@
 }
 
+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)->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)->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)->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)->GetNumberOfComponents() == 3) {
+                TRACE("Setting cell vectors to '%s'", _dataSet->GetCellData()->GetArrayName(i));
+                _dataSet->GetCellData()->SetActiveVectors(_dataSet->GetCellData()->GetArrayName(i));
+                break;
+            }
+        }
+    }
+}
+
 /**
  * \brief Read dataset using supplied reader
@@ -183,4 +232,6 @@
         ERROR("No lookup table should be specified in DataSets");
     }
+
+    setDefaultArrays();
 
 #ifdef WANT_TRACE
@@ -205,4 +256,6 @@
         ERROR("No lookup table should be specified in DataSets");
     }
+
+    setDefaultArrays();
 
 #ifdef WANT_TRACE
@@ -250,9 +303,83 @@
  * \brief Does DataSet lie in the XY plane (z = 0)
  */
-bool DataSet::is2D() const
+bool DataSet::isXY() const
 {
     double bounds[6];
     getBounds(bounds);
     return (bounds[4] == 0. && bounds[4] == bounds[5]);
+}
+
+/**
+ * \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(DataSet::PrincipalPlane *plane, double *offset) const
+{
+    double bounds[6];
+    getBounds(bounds);
+    if (bounds[4] == bounds[5]) {
+        // Z = 0, XY plane
+        if (plane != NULL) {
+            *plane = PLANE_XY;
+        }
+        if (offset != NULL)
+            *offset = bounds[4];
+        return true;
+    } else if (bounds[0] == bounds[1]) {
+        // X = 0, ZY plane
+        if (plane != NULL) {
+            *plane = PLANE_ZY;
+        }
+        if (offset != NULL)
+            *offset = bounds[0];
+        return true;
+    } else if (bounds[2] == bounds[3]) {
+        // Y = 0, 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
+ */
+DataSet::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;
+    }
 }
 
@@ -324,5 +451,13 @@
 void DataSet::getScalarRange(double minmax[2]) const
 {
-    _dataSet->GetScalarRange(minmax);
+    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);
+    }
 }
 
@@ -419,13 +554,43 @@
  * \return the value of the nearest point or 0 if no scalar data available
  */
-double DataSet::getDataValue(double x, double y, double z) const
+bool DataSet::getScalarValue(double x, double y, double z, double *value) const
 {
     if (_dataSet == NULL)
-        return 0;
+        return false;
     if (_dataSet->GetPointData() == NULL ||
         _dataSet->GetPointData()->GetScalars() == NULL) {
-        return 0.0;
+        return false;
     }
     vtkIdType pt = _dataSet->FindPoint(x, y, z);
-    return _dataSet->GetPointData()->GetScalars()->GetComponent(pt, 0);
-}
+    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;
+}
Index: /trunk/packages/vizservers/vtkvis/RpVtkDataSet.h
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpVtkDataSet.h	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpVtkDataSet.h	(revision 2423)
@@ -24,4 +24,9 @@
 class DataSet {
 public:
+    enum PrincipalPlane {
+        PLANE_XY,
+        PLANE_ZY,
+        PLANE_XZ
+    };
     DataSet(const std::string& name);
     virtual ~DataSet();
@@ -37,5 +42,11 @@
     vtkDataSet *copyData(vtkDataSet *ds);
 
-    bool is2D() const;
+    bool isXY() const;
+
+    int numDimensions() const;
+
+    bool is2D(PrincipalPlane *plane = NULL, double *offset = NULL) const;
+
+    PrincipalPlane principalPlane() const;
 
     const std::string& getName() const;
@@ -59,5 +70,7 @@
     void getCellSizeRange(double minmax[6], double *average) const;
 
-    double getDataValue(double x, double y, double z) const;
+    bool getScalarValue(double x, double y, double z, double *value) const;
+
+    bool getVectorValue(double x, double y, double z, double vector[3]) const;
 
     void setVisibility(bool state);
@@ -67,4 +80,6 @@
 private:
     DataSet();
+
+    void setDefaultArrays();
     void print() const;
 
Index: /trunk/packages/vizservers/vtkvis/RpVtkGraphicsObject.h
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpVtkGraphicsObject.h	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpVtkGraphicsObject.h	(revision 2423)
@@ -46,4 +46,5 @@
         _opacity(1.0),
         _edgeWidth(1.0f),
+        _pointSize(1.0f),
         _lighting(true),
         _cullFace(CULL_BACK),
@@ -607,5 +608,5 @@
      * \brief Set pixel width of edges
      *
-     * NOTE: May be a no-op if OpenGL implementation doesn't support fat lines
+     * NOTE: May be a no-op if OpenGL implementation doesn't support wide lines
      */
     virtual void setEdgeWidth(float width)
@@ -621,4 +622,26 @@
                 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);
                 }
             }
@@ -748,4 +771,5 @@
     float _edgeColor[3];
     float _edgeWidth;
+    float _pointSize;
     bool _lighting;
     CullFace _cullFace;
Index: /trunk/packages/vizservers/vtkvis/RpVtkRenderServer.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpVtkRenderServer.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpVtkRenderServer.cpp	(revision 2423)
@@ -70,5 +70,5 @@
     if (g_renderer->getCameraMode() == Renderer::IMAGE) {
         double xywh[4];
-        g_renderer->getScreenWorldCoords(xywh);
+        g_renderer->getCameraZoomRegion(xywh);
         std::ostringstream oss;
         oss.precision(12);
@@ -77,7 +77,7 @@
             << std::scientific
             << xywh[0] << " "
-            << (xywh[1] + xywh[3]) << " "
-            << (xywh[0] + xywh[2]) << " "
-            << xywh[1] << "} -bytes";
+            << xywh[1] << " "
+            << xywh[2] << " "
+            << xywh[3] << "} -bytes";
 
 #ifdef RENDER_TARGA
Index: /trunk/packages/vizservers/vtkvis/RpVtkRenderer.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpVtkRenderer.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpVtkRenderer.cpp	(revision 2423)
@@ -24,8 +24,8 @@
 #include <vtkCharArray.h>
 #include <vtkAxisActor2D.h>
+#include <vtkCubeAxesActor.h>
 #ifdef USE_CUSTOM_AXES
-#include <vtkRpCubeAxesActor2D.h>
+#include "vtkRpCubeAxesActor2D.h"
 #else
-#include <vtkCubeAxesActor.h>
 #include <vtkCubeAxesActor2D.h>
 #endif
@@ -53,6 +53,8 @@
 Renderer::Renderer() :
     _needsRedraw(true),
-    _windowWidth(320),
-    _windowHeight(320),
+    _windowWidth(500),
+    _windowHeight(500),
+    _imgCameraPlane(PLANE_XY),
+    _imgCameraOffset(0),
     _cameraZoomRatio(1),
     _useCumulativeRange(true),
@@ -657,4 +659,13 @@
 }
 
+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
@@ -850,4 +861,5 @@
     _cubeAxesActor2D->GetXAxisActor2D()->AdjustLabelsOn();
     _cubeAxesActor2D->GetYAxisActor2D()->AdjustLabelsOn();
+    _cubeAxesActor2D->GetZAxisActor2D()->AdjustLabelsOn();
 
 #ifdef USE_CUSTOM_AXES
@@ -869,4 +881,12 @@
     _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ItalicOff();
     _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ShadowOff();
+
+    //_cubeAxesActor2D->GetZAxisActor2D()->SizeFontRelativeToAxisOn();
+    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->BoldOn();
+    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->ItalicOff();
+    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->ShadowOn();
+    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->BoldOff();
+    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->ItalicOff();
+    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->ShadowOff();
 #else
     _cubeAxesActor2D->GetAxisTitleTextProperty()->BoldOn();
@@ -1459,5 +1479,5 @@
  * \brief Create a new Contour2D and associate it with the named DataSet
  */
-void Renderer::addContour2D(const DataSetId& id, int numContours)
+bool Renderer::addContour2D(const DataSetId& id, int numContours)
 {
     DataSetHashmap::iterator itr;
@@ -1472,5 +1492,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -1485,6 +1505,5 @@
 
         Contour2D *contour = new Contour2D(numContours);
-        _contour2Ds[dsID] = contour;
-
+ 
         contour->setDataSet(ds,
                             _useCumulativeRange, 
@@ -1493,9 +1512,17 @@
                             _cumulativeVectorComponentRange);
 
-        _renderer->AddViewProp(contour->getProp());
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour2Ds[dsID] = contour;
     } while (doAll && ++itr != _dataSets.end());
 
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -1503,5 +1530,5 @@
  * \brief Create a new Contour2D and associate it with the named DataSet
  */
-void Renderer::addContour2D(const DataSetId& id, const std::vector<double>& contours)
+bool Renderer::addContour2D(const DataSetId& id, const std::vector<double>& contours)
 {
     DataSetHashmap::iterator itr;
@@ -1516,5 +1543,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -1529,5 +1556,4 @@
 
         Contour2D *contour = new Contour2D(contours);
-        _contour2Ds[dsID] = contour;
 
         contour->setDataSet(ds,
@@ -1537,9 +1563,17 @@
                             _cumulativeVectorComponentRange);
 
-        _renderer->AddViewProp(contour->getProp());
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour2Ds[dsID] = contour;
     } while (doAll && ++itr != _dataSets.end());
 
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -1894,5 +1928,5 @@
  * \brief Create a new Contour3D and associate it with the named DataSet
  */
-void Renderer::addContour3D(const DataSetId& id, int numContours)
+bool Renderer::addContour3D(const DataSetId& id, int numContours)
 {
     DataSetHashmap::iterator itr;
@@ -1907,5 +1941,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -1920,5 +1954,4 @@
 
         Contour3D *contour = new Contour3D(numContours);
-        _contour3Ds[dsID] = contour;
 
         contour->setDataSet(ds,
@@ -1928,5 +1961,12 @@
                             _cumulativeVectorComponentRange);
 
-        _renderer->AddViewProp(contour->getProp());
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour3Ds[dsID] = contour;
     } while (doAll && ++itr != _dataSets.end());
 
@@ -1935,4 +1975,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -1940,5 +1981,5 @@
  * \brief Create a new Contour3D and associate it with the named DataSet
  */
-void Renderer::addContour3D(const DataSetId& id,const std::vector<double>& contours)
+bool Renderer::addContour3D(const DataSetId& id,const std::vector<double>& contours)
 {
     DataSetHashmap::iterator itr;
@@ -1953,5 +1994,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -1966,5 +2007,4 @@
 
         Contour3D *contour = new Contour3D(contours);
-        _contour3Ds[dsID] = contour;
 
         contour->setDataSet(ds,
@@ -1974,5 +2014,12 @@
                             _cumulativeVectorComponentRange);
 
-        _renderer->AddViewProp(contour->getProp());
+        if (contour->getProp() == NULL) {
+            delete contour;
+            return false;
+        } else {
+            _renderer->AddViewProp(contour->getProp());
+        }
+
+        _contour3Ds[dsID] = contour;
     } while (doAll && ++itr != _dataSets.end());
 
@@ -1981,4 +2028,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -2453,5 +2501,5 @@
  * \brief Create a new Glyphs and associate it with the named DataSet
  */
-void Renderer::addGlyphs(const DataSetId& id, Glyphs::GlyphShape shape)
+bool Renderer::addGlyphs(const DataSetId& id, Glyphs::GlyphShape shape)
 {
     DataSetHashmap::iterator itr;
@@ -2466,5 +2514,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -2479,5 +2527,4 @@
 
         Glyphs *glyphs = new Glyphs(shape);
-        _glyphs[dsID] = glyphs;
 
         glyphs->setDataSet(ds,
@@ -2487,5 +2534,12 @@
                            _cumulativeVectorComponentRange);
 
-        _renderer->AddViewProp(glyphs->getProp());
+        if (glyphs->getProp() == NULL) {
+            delete glyphs;
+            return false;
+        } else {
+            _renderer->AddViewProp(glyphs->getProp());
+        }
+
+        _glyphs[dsID] = glyphs;
     } while (doAll && ++itr != _dataSets.end());
 
@@ -2495,4 +2549,5 @@
 
     _needsRedraw = true;
+    return true;
 }
 
@@ -3022,5 +3077,5 @@
  * \brief Create a new HeightMap and associate it with the named DataSet
  */
-void Renderer::addHeightMap(const DataSetId& id, int numContours)
+bool Renderer::addHeightMap(const DataSetId& id, int numContours, double heightScale)
 {
     DataSetHashmap::iterator itr;
@@ -3035,5 +3090,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -3047,6 +3102,5 @@
         }
 
-        HeightMap *hmap = new HeightMap(numContours);
-        _heightMaps[dsID] = hmap;
+        HeightMap *hmap = new HeightMap(numContours, heightScale);
 
         hmap->setDataSet(ds,
@@ -3056,5 +3110,12 @@
                          _cumulativeVectorComponentRange);
 
-        _renderer->AddViewProp(hmap->getProp());
+        if (hmap->getProp() == NULL) {
+            delete hmap;
+            return false;
+        } else {
+            _renderer->AddViewProp(hmap->getProp());
+        }
+
+        _heightMaps[dsID] = hmap;
     } while (doAll && ++itr != _dataSets.end());
 
@@ -3064,4 +3125,5 @@
 
     _needsRedraw = true;
+    return true;
 }
 
@@ -3069,5 +3131,5 @@
  * \brief Create a new HeightMap and associate it with the named DataSet
  */
-void Renderer::addHeightMap(const DataSetId& id, const std::vector<double>& contours)
+bool Renderer::addHeightMap(const DataSetId& id, const std::vector<double>& contours, double heightScale)
 {
     DataSetHashmap::iterator itr;
@@ -3082,5 +3144,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -3094,6 +3156,5 @@
         }
 
-        HeightMap *hmap = new HeightMap(contours);
-        _heightMaps[dsID] = hmap;
+        HeightMap *hmap = new HeightMap(contours, heightScale);
 
         hmap->setDataSet(ds,
@@ -3103,5 +3164,12 @@
                          _cumulativeVectorComponentRange);
 
-        _renderer->AddViewProp(hmap->getProp());
+        if (hmap->getProp() == NULL) {
+            delete hmap;
+            return false;
+        } else {
+            _renderer->AddViewProp(hmap->getProp());
+        }
+
+        _heightMaps[dsID] = hmap;
     } while (doAll && ++itr != _dataSets.end());
 
@@ -3111,4 +3179,5 @@
 
     _needsRedraw = true;
+    return true;
 }
 
@@ -3368,5 +3437,5 @@
  * \brief Set the number of equally spaced contour isolines for the given DataSet
  */
-void Renderer::setHeightMapContours(const DataSetId& id, int numContours)
+void Renderer::setHeightMapNumContours(const DataSetId& id, int numContours)
 {
     HeightMapHashmap::iterator itr;
@@ -3386,5 +3455,5 @@
 
     do {
-        itr->second->setContours(numContours);
+        itr->second->setNumContours(numContours);
     } while (doAll && ++itr != _heightMaps.end());
 
@@ -3468,4 +3537,31 @@
     do {
         itr->second->setVisibility(state);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set wireframe rendering for the specified DataSet
+ */
+void Renderer::setHeightMapWireframe(const DataSetId& id, bool state)
+{
+    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->setWireframe(state);
     } while (doAll && ++itr != _heightMaps.end());
 
@@ -3560,5 +3656,5 @@
  * \brief Turn on/off rendering height map contour lines for the given DataSet
  */
-void Renderer::setHeightMapContourVisibility(const DataSetId& id, bool state)
+void Renderer::setHeightMapContourLineVisibility(const DataSetId& id, bool state)
 {
     HeightMapHashmap::iterator itr;
@@ -3578,5 +3674,32 @@
 
     do {
-        itr->second->setContourVisibility(state);
+        itr->second->setContourLineVisibility(state);
+    } while (doAll && ++itr != _heightMaps.end());
+
+    _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();
+        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());
 
@@ -3670,5 +3793,5 @@
  * \brief Create a new LIC and associate it with the named DataSet
  */
-void Renderer::addLIC(const DataSetId& id)
+bool Renderer::addLIC(const DataSetId& id)
 {
     DataSetHashmap::iterator itr;
@@ -3683,5 +3806,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -3711,4 +3834,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -4103,5 +4227,5 @@
  * \brief Create a new Molecule and associate it with the named DataSet
  */
-void Renderer::addMolecule(const DataSetId& id)
+bool Renderer::addMolecule(const DataSetId& id)
 {
     DataSetHashmap::iterator itr;
@@ -4116,5 +4240,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -4140,4 +4264,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -4611,5 +4736,5 @@
  * \brief Create a new PolyData and associate it with the named DataSet
  */
-void Renderer::addPolyData(const DataSetId& id)
+bool Renderer::addPolyData(const DataSetId& id)
 {
     DataSetHashmap::iterator itr;
@@ -4624,5 +4749,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -4648,4 +4773,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -4971,7 +5097,10 @@
 
 /**
- * \brief Set wireframe rendering for the specified DataSet
- */
-void Renderer::setPolyDataWireframe(const DataSetId& id, bool state)
+ * \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.
+ */
+void Renderer::setPolyDataPointSize(const DataSetId& id, float size)
 {
     PolyDataHashmap::iterator itr;
@@ -4991,4 +5120,31 @@
 
     do {
+        itr->second->setPointSize(size);
+    } while (doAll && ++itr != _polyDatas.end());
+
+    _needsRedraw = true;
+}
+
+/**
+ * \brief Set wireframe rendering for the specified DataSet
+ */
+void Renderer::setPolyDataWireframe(const DataSetId& id, bool state)
+{
+    PolyDataHashmap::iterator itr;
+
+    bool doAll = false;
+
+    if (id.compare("all") == 0) {
+        itr = _polyDatas.begin();
+        doAll = true;
+    } else {
+        itr = _polyDatas.find(id);
+    }
+    if (itr == _polyDatas.end()) {
+        ERROR("PolyData not found: %s", id.c_str());
+        return;
+    }
+
+    do {
         itr->second->setWireframe(state);
     } while (doAll && ++itr != _polyDatas.end());
@@ -5027,5 +5183,5 @@
  * \brief Create a new PseudoColor rendering for the specified DataSet
  */
-void Renderer::addPseudoColor(const DataSetId& id)
+bool Renderer::addPseudoColor(const DataSetId& id)
 {
     DataSetHashmap::iterator itr;
@@ -5040,5 +5196,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -5065,4 +5221,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -5461,5 +5618,5 @@
  * \brief Create a new Streamlines and associate it with the named DataSet
  */
-void Renderer::addStreamlines(const DataSetId& id)
+bool Renderer::addStreamlines(const DataSetId& id)
 {
     DataSetHashmap::iterator itr;
@@ -5474,5 +5631,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -5500,4 +5657,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -6230,5 +6388,5 @@
  * \brief Create a new Volume and associate it with the named DataSet
  */
-void Renderer::addVolume(const DataSetId& id)
+bool Renderer::addVolume(const DataSetId& id)
 {
     DataSetHashmap::iterator itr;
@@ -6243,5 +6401,5 @@
     if (itr == _dataSets.end()) {
         ERROR("Unknown dataset %s", id.c_str());
-        return;
+        return false;
     }
 
@@ -6271,4 +6429,5 @@
     initCamera();
     _needsRedraw = true;
+    return true;
 }
 
@@ -6652,5 +6811,9 @@
 void Renderer::setWindowSize(int width, int height)
 {
-    setViewAngle(height);
+    if (_windowWidth == width &&
+        _windowHeight == height)
+        return;
+
+    //setViewAngle(height);
 
     // FIXME: Fix up panning on aspect change
@@ -6691,4 +6854,6 @@
     CameraMode origMode = _cameraMode;
     _cameraMode = mode;
+    resetAxes();
+
     vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
     switch (mode) {
@@ -6719,6 +6884,4 @@
         ERROR("Unkown camera mode: %d", mode);
     }
-    resetAxes();
-
     _needsRedraw = true;
 }
@@ -7173,9 +7336,10 @@
         setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
                             _imgWorldDims[0], _imgWorldDims[1]);
-    } else if (_cameraMode == ORTHO) {
-        camera->Zoom(z); // Change ortho parallel scale (Dolly has no effect in ortho)
-        _renderer->ResetCameraClippingRange();
-    } else {
-        camera->Dolly(z); // Move camera forward/back
+    } 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);
         _renderer->ResetCameraClippingRange();
         //computeScreenWorldCoords();
@@ -7186,4 +7350,31 @@
 
     _needsRedraw = 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)
+{
+    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);
 }
 
@@ -7200,7 +7391,10 @@
     double camPos[2];
 
-    int pxOffsetX = 85;
-    int pxOffsetY = 75;
-    int outerGutter = 15;
+    int pxOffsetX = (int)(0.17 * (double)_windowWidth);
+    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
+    int pxOffsetY = (int)(0.15 * (double)_windowHeight);
+    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
+    int outerGutter = (int)(0.03 * (double)_windowWidth);
+    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
 
     int imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
@@ -7238,22 +7432,78 @@
     vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
     camera->ParallelProjectionOn();
-    camera->SetPosition(camPos[0], camPos[1], 1);
-    camera->SetFocalPoint(camPos[0], camPos[1], 0);
-    camera->SetViewUp(0, 1, 0);
     camera->SetClippingRange(1, 2);
     // Half of world coordinate height of viewport (Documentation is wrong)
     camera->SetParallelScale(_windowHeight * pxToWorld / 2.0);
 
-    // bottom
-    _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
-    // left
-    _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
-    // top
-    _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
-    // right
-    _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
-
-    _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
-                                _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1], 0, 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] + _imgWorldDims[1], 0);
+        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
+        // right
+        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
+        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
+        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
+                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
+                                    _imgCameraOffset, _imgCameraOffset);
+        _cubeAxesActor2D->XAxisVisibilityOn();
+        _cubeAxesActor2D->YAxisVisibilityOn();
+        _cubeAxesActor2D->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] + _imgWorldDims[1], 0);
+        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
+        // right
+        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWorldDims[0]);
+        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
+        _cubeAxesActor2D->SetBounds(_imgCameraOffset, _imgCameraOffset, 
+                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
+                                    _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0]);
+        _cubeAxesActor2D->XAxisVisibilityOff();
+        _cubeAxesActor2D->YAxisVisibilityOn();
+        _cubeAxesActor2D->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] + _imgWorldDims[1]);
+        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
+        // right
+        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
+        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
+        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
+                                    _imgCameraOffset, _imgCameraOffset,
+                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1]);
+        _cubeAxesActor2D->XAxisVisibilityOn();
+        _cubeAxesActor2D->YAxisVisibilityOff();
+        _cubeAxesActor2D->ZAxisVisibilityOn();
+    }
 
     // Compute screen world coordinates
@@ -7347,8 +7597,21 @@
     mat->Delete();
 
-    _screenWorldCoords[0] = x0;
-    _screenWorldCoords[1] = y0;
-    _screenWorldCoords[2] = x1 - x0;
-    _screenWorldCoords[3] = y1 - y0;
+    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;
+    }
 }
 
@@ -7465,13 +7728,27 @@
             mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
     }
-    for (int i = 0; i < 6; i++) {
-        if (i % 2 == 0) {
-            if (bounds[i] == DBL_MAX)
-                bounds[i] = 0;
-        } else {
-            if (bounds[i] == -DBL_MAX)
-                bounds[i] = 1;
+
+    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],
@@ -7564,4 +7841,16 @@
         collectVectorComponentRanges(_cumulativeVectorComponentRange[i], i,
                                      _cumulativeRangeOnlyVisible);
+    }
+
+    TRACE("Cumulative scalar range: %g, %g",
+          _cumulativeScalarRange[0],
+          _cumulativeScalarRange[1]);
+    TRACE("Cumulative vmag range: %g, %g",
+          _cumulativeVectorMagnitudeRange[0],
+          _cumulativeVectorMagnitudeRange[1]);
+    for (int i = 0; i < 3; i++) {
+        TRACE("Cumulative v[%d] range: %g, %g", i,
+              _cumulativeVectorComponentRange[i][0],
+              _cumulativeVectorComponentRange[i][1]);
     }
 }
@@ -7644,4 +7933,39 @@
         range[1] = 1;
  }
+
+/**
+ * \brief Determines if AABB lies in a principal axis plane
+ * and if so, returns the plane normal
+ */
+bool Renderer::is2D(const double bounds[6],
+                    Renderer::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;
+    return false;
+}
 
 /**
@@ -7667,8 +7991,30 @@
     double bounds[6];
     collectBounds(bounds, false);
-    _imgWorldOrigin[0] = bounds[0];
-    _imgWorldOrigin[1] = bounds[2];
-    _imgWorldDims[0] = bounds[1] - bounds[0];
-    _imgWorldDims[1] = bounds[3] - bounds[2];
+    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
+    if (twod) {
+        _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;
@@ -7697,4 +8043,5 @@
         ERROR("Unknown camera mode");
     }
+
 #ifdef WANT_TRACE
     printCameraInfo(_renderer->GetActiveCamera());
@@ -7707,7 +8054,8 @@
 void Renderer::printCameraInfo(vtkCamera *camera)
 {
-    TRACE("Parallel Scale: %g, View angle: %g, Cam pos: %g %g %g, focal pt: %g %g %g, view up: %g %g %g, Clipping range: %g %g",
+    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],
@@ -8096,17 +8444,17 @@
  * Note: no interpolation is performed on data
  */
-double Renderer::getDataValueAtPixel(const DataSetId& id, int x, int y)
+bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
 {
     vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
     coord->SetCoordinateSystemToDisplay();
-    coord->SetValue(x, y, 0);
+    coord->SetValue(x, _windowHeight - y, 0);
     double *worldCoords = coord->GetComputedWorldValue(_renderer);
 
-    TRACE("Pixel coords: %d, %d\nWorld coords: %.12e, %.12e, %12e", x, y,
+    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
           worldCoords[0], 
           worldCoords[1], 
           worldCoords[2]);
 
-    return getDataValue(id, worldCoords[0], worldCoords[1], worldCoords[2]);
+    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
 }
 
@@ -8116,11 +8464,44 @@
  * Note: no interpolation is performed on data
  */
-double Renderer::getDataValue(const DataSetId& id, double x, double y, double z)
+bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
 {
     DataSet *ds = getDataSet(id);
     if (ds == NULL)
-        return 0;
-    vtkDataSet *vtkds = ds->getVtkDataSet(); 
-    vtkIdType pt = vtkds->FindPoint(x, y, z);
-    return vtkds->GetPointData()->GetScalars()->GetComponent(pt, 0);
-}
+        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: /trunk/packages/vizservers/vtkvis/RpVtkRenderer.h
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpVtkRenderer.h	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpVtkRenderer.h	(revision 2423)
@@ -12,5 +12,5 @@
 #include <vtkCubeAxesActor.h>
 #ifdef USE_CUSTOM_AXES
-#include <vtkRpCubeAxesActor2D.h>
+#include "vtkRpCubeAxesActor2D.h"
 #else
 #include <vtkCubeAxesActor2D.h>
@@ -59,4 +59,10 @@
         Y_AXIS,
         Z_AXIS
+    };
+
+    enum PrincipalPlane {
+        PLANE_XY,
+        PLANE_ZY,
+        PLANE_XZ
     };
 
@@ -104,4 +110,6 @@
     DataSet *getDataSet(const DataSetId& id);
 
+    void getDataSetNames(std::vector<std::string>& names);
+
     bool setData(const DataSetId& id, char *data, int nbytes);
 
@@ -112,7 +120,11 @@
     bool setDataSetActiveVectors(const DataSetId& id, const char *vectorName);
 
-    double getDataValueAtPixel(const DataSetId& id, int x, int y);
-
-    double getDataValue(const DataSetId& id, double x, double y, double z);
+    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 setOpacity(const DataSetId& id, double opacity);
@@ -148,4 +160,6 @@
     void resetCameraClippingRange();
 
+    void setCameraZoomRegionPixels(int x, int y, int width, int height);
+
     void setCameraZoomRegion(double x, double y, double width, double height);
 
@@ -232,7 +246,7 @@
     // 2D Contour plots
 
-    void addContour2D(const DataSetId& id, int numContours);
-
-    void addContour2D(const DataSetId& id, const std::vector<double>& contours);
+    bool addContour2D(const DataSetId& id, int numContours);
+
+    bool addContour2D(const DataSetId& id, const std::vector<double>& contours);
 
     void deleteContour2D(const DataSetId& id);
@@ -266,7 +280,7 @@
     // 3D Contour (isosurface) plots
 
-    void addContour3D(const DataSetId& id, int numContours);
-
-    void addContour3D(const DataSetId& id, const std::vector<double>& contours);
+    bool addContour3D(const DataSetId& id, int numContours);
+
+    bool addContour3D(const DataSetId& id, const std::vector<double>& contours);
 
     void deleteContour3D(const DataSetId& id);
@@ -308,5 +322,5 @@
     // Glyphs
 
-    void addGlyphs(const DataSetId& id, Glyphs::GlyphShape shape);
+    bool addGlyphs(const DataSetId& id, Glyphs::GlyphShape shape);
 
     void deleteGlyphs(const DataSetId& id);
@@ -352,7 +366,7 @@
     // Height maps
 
-    void addHeightMap(const DataSetId& id, int numContours);
-
-    void addHeightMap(const DataSetId& id, const std::vector<double>& contours);
+    bool addHeightMap(const DataSetId& id, int numContours, double heightScale);
+
+    bool addHeightMap(const DataSetId& id, const std::vector<double>& contours, double heightScale);
 
     void deleteHeightMap(const DataSetId& id);
@@ -382,4 +396,6 @@
     void setHeightMapVisibility(const DataSetId& id, bool state);
 
+    void setHeightMapWireframe(const DataSetId& id, bool state);
+
     void setHeightMapVolumeSlice(const DataSetId& id, HeightMap::Axis axis, double ratio);
 
@@ -388,9 +404,11 @@
     void setHeightMapColorMap(const DataSetId& id, const ColorMapId& colorMapId);
 
-    void setHeightMapContours(const DataSetId& id, int numContours);
+    void setHeightMapNumContours(const DataSetId& id, int numContours);
 
     void setHeightMapContourList(const DataSetId& id, const std::vector<double>& contours);
 
-    void setHeightMapContourVisibility(const DataSetId& id, bool state);
+    void setHeightMapContourSurfaceVisibility(const DataSetId& id, bool state);
+
+    void setHeightMapContourLineVisibility(const DataSetId& id, bool state);
 
     void setHeightMapContourEdgeColor(const DataSetId& id, float color[3]);
@@ -400,5 +418,5 @@
     // LIC plots
 
-    void addLIC(const DataSetId& id);
+    bool addLIC(const DataSetId& id);
     
     void deleteLIC(const DataSetId& id);
@@ -434,5 +452,5 @@
     // Molecules
 
-    void addMolecule(const DataSetId& id);
+    bool addMolecule(const DataSetId& id);
     
     void deleteMolecule(const DataSetId& id);
@@ -474,5 +492,5 @@
     // PolyData Meshes
 
-    void addPolyData(const DataSetId& id);
+    bool addPolyData(const DataSetId& id);
     
     void deletePolyData(const DataSetId& id);
@@ -498,4 +516,6 @@
     void setPolyDataEdgeWidth(const DataSetId& id, float edgeWidth);
 
+    void setPolyDataPointSize(const DataSetId& id, float size);
+
     void setPolyDataLighting(const DataSetId& id, bool state);
 
@@ -509,5 +529,5 @@
     // Color-mapped surfaces
 
-    void addPseudoColor(const DataSetId& id);
+    bool addPseudoColor(const DataSetId& id);
 
     void deletePseudoColor(const DataSetId& id);
@@ -543,5 +563,5 @@
     // Streamlines
 
-    void addStreamlines(const DataSetId& id);
+    bool addStreamlines(const DataSetId& id);
 
     void deleteStreamlines(const DataSetId& id);
@@ -612,5 +632,5 @@
     // Volumes
 
-    void addVolume(const DataSetId& id);
+    bool addVolume(const DataSetId& id);
 
     void deleteVolume(const DataSetId& id);
@@ -678,4 +698,7 @@
     void computeScreenWorldCoords();
 
+    bool is2D(const double bounds[6],
+              PrincipalPlane *plane,
+              double *offset) const;
     void initCamera();
     void initAxes();
@@ -687,4 +710,6 @@
     double _imgWorldOrigin[2];
     double _imgWorldDims[2];
+    PrincipalPlane _imgCameraPlane;
+    double _imgCameraOffset;
     double _screenWorldCoords[4];
     double _cameraOrientation[4];
Index: /trunk/packages/vizservers/vtkvis/RpVtkRendererCmd.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/RpVtkRendererCmd.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/RpVtkRendererCmd.cpp	(revision 2423)
@@ -10,4 +10,6 @@
 #include <cstring>
 #include <cerrno>
+#include <string>
+#include <sstream>
 #include <unistd.h>
 #include <sys/uio.h>
@@ -24,4 +26,20 @@
 using namespace Rappture::VtkVis;
 
+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;
+}
+
 static int
 ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr) 
@@ -35,5 +53,5 @@
 }
 
-static bool
+static int
 GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
 {
@@ -47,5 +65,5 @@
 }
 
-int
+static int
 GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
 {
@@ -324,14 +342,9 @@
 
     char buf[256];
-    snprintf(buf, sizeof(buf), "nv>camera set %.12e %.12e %.12e %.12e %.12e %.12e %.12e %.12e %.12e", 
+    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]);
-    ssize_t bytesWritten;
-    size_t len = strlen(buf);
-    size_t ofs = 0;
-    while ((bytesWritten = write(g_fdOut, buf + ofs, len - ofs)) > 0) {
-        ofs += bytesWritten;
-        if (ofs == len)
-            break;
-    }
+
+    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
+
     if (bytesWritten < 0) {
         return TCL_ERROR;
@@ -361,14 +374,30 @@
               Tcl_Obj *const *objv)
 {
-    double x, y, width, height;
-
-    if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK ||
-        Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK ||
-        Tcl_GetDoubleFromObj(interp, objv[4], &width) != TCL_OK ||
-        Tcl_GetDoubleFromObj(interp, objv[5], &height) != TCL_OK) {
-        return TCL_ERROR;
-    }
-
-    g_renderer->setCameraZoomRegion(x, y, width, height);
+    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;
+        }
+        g_renderer->setCameraZoomRegionPixels(x, y, width, height);
+    } 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;
+        }
+        g_renderer->setCameraZoomRegion(x, y, width, height);
+    } else {
+        Tcl_AppendResult(interp, "bad camera ortho option \"", string,
+                         "\": should be pixel or world", (char*)NULL);
+        return TCL_ERROR;
+    }
+
     return TCL_OK;
 }
@@ -466,5 +495,5 @@
     {"mode", 1, CameraModeOp, 3, 3, "mode"},
     {"orient", 3, CameraOrientOp, 6, 6, "qw qx qy qz"},
-    {"ortho", 1, CameraOrthoOp, 6, 6, "x y width height"},
+    {"ortho", 1, CameraOrthoOp, 7, 7, "coordMode x y width height"},
     {"pan", 1, CameraPanOp, 4, 4, "panX panY"},
     {"reset", 2, CameraResetOp, 2, 3, "?all?"},
@@ -625,7 +654,13 @@
     if (objc == 5) {
         const char *name = Tcl_GetString(objv[4]);
-        g_renderer->addContour2D(name, contourList);
-    } else {
-        g_renderer->addContour2D("all", contourList);
+        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;
@@ -642,7 +677,13 @@
     if (objc == 5) {
         const char *name = Tcl_GetString(objv[4]);
-        g_renderer->addContour2D(name, numContours);
-    } else {
-        g_renderer->addContour2D("all", numContours);
+        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;
@@ -878,7 +919,13 @@
     if (objc == 5) {
         const char *name = Tcl_GetString(objv[4]);
-        g_renderer->addContour3D(name, contourList);
-    } else {
-        g_renderer->addContour3D("all", contourList);
+        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;
@@ -895,7 +942,13 @@
     if (objc == 5) {
         const char *name = Tcl_GetString(objv[4]);
-        g_renderer->addContour3D(name, numContours);
-    } else {
-        g_renderer->addContour3D("all", numContours);
+        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;
@@ -1290,6 +1343,6 @@
 
 static int
-DataSetGetValuePixelOp(ClientData clientData, Tcl_Interp *interp, int objc, 
-                       Tcl_Obj *const *objv)
+DataSetGetScalarPixelOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
 {
     const char *name = Tcl_GetString(objv[5]);
@@ -1299,15 +1352,15 @@
         return TCL_ERROR;
     }
-    double value = g_renderer->getDataValueAtPixel(name, x, y);
-    char buf[128];
-    snprintf(buf, sizeof(buf), "nv>dataset value pixel %d %d %.12e", x, y, value);
-    ssize_t bytesWritten;
-    size_t len = strlen(buf);
-    size_t ofs = 0;
-    while ((bytesWritten = write(g_fdOut, buf + ofs, len - ofs)) > 0) {
-        ofs += bytesWritten;
-        if (ofs == len)
-            break;
-    }
+    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];
+    snprintf(buf, sizeof(buf), "nv>dataset scalar pixel %d %d %g %s\n", x, y, value, name);
+
+    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
+
     if (bytesWritten < 0) {
         return TCL_ERROR;
@@ -1317,6 +1370,6 @@
 
 static int
-DataSetGetValueWorldOp(ClientData clientData, Tcl_Interp *interp, int objc, 
-                       Tcl_Obj *const *objv)
+DataSetGetScalarWorldOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                        Tcl_Obj *const *objv)
 {
     const char *name = Tcl_GetString(objv[6]);
@@ -1327,15 +1380,15 @@
         return TCL_ERROR;
     }
-    double value = g_renderer->getDataValue(name, x, y, z);
-    char buf[128];
-    snprintf(buf, sizeof(buf), "nv>dataset value world %.12e %.12e %.12e %.12e", x, y, z, value);
-    ssize_t bytesWritten;
-    size_t len = strlen(buf);
-    size_t ofs = 0;
-    while ((bytesWritten = write(g_fdOut, buf + ofs, len - ofs)) > 0) {
-        ofs += bytesWritten;
-        if (ofs == len)
-            break;
-    }
+    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];
+    snprintf(buf, sizeof(buf), "nv>dataset scalar world %g %g %g %g %s\n", x, y, z, value, name);
+
+    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
+
     if (bytesWritten < 0) {
         return TCL_ERROR;
@@ -1344,17 +1397,94 @@
 }
 
-static Rappture::CmdSpec dataSetGetValueOps[] = {
-    {"pixel", 1, DataSetGetValuePixelOp, 6, 6, "x y dataSetName"},
-    {"world", 1, DataSetGetValueWorldOp, 7, 7, "x y z dataSetName"}
+static Rappture::CmdSpec dataSetGetScalarOps[] = {
+    {"pixel", 1, DataSetGetScalarPixelOp, 6, 6, "x y dataSetName"},
+    {"world", 1, DataSetGetScalarWorldOp, 7, 7, "x y z dataSetName"}
 };
-static int nDataSetGetValueOps = NumCmdSpecs(dataSetGetValueOps);
-
-static int
-DataSetGetValueOp(ClientData clientData, Tcl_Interp *interp, int objc, 
-                  Tcl_Obj *const *objv)
+static int nDataSetGetScalarOps = NumCmdSpecs(dataSetGetScalarOps);
+
+static int
+DataSetGetScalarOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+                   Tcl_Obj *const *objv)
 {
     Tcl_ObjCmdProc *proc;
 
-    proc = Rappture::GetOpFromObj(interp, nDataSetGetValueOps, dataSetGetValueOps,
+    proc = Rappture::GetOpFromObj(interp, nDataSetGetScalarOps, dataSetGetScalarOps,
+                                  Rappture::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];
+    snprintf(buf, sizeof(buf), "nv>dataset vector pixel %d %d %g %g %g %s\n", x, y,
+             value[0], value[1], value[2], name);
+
+    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
+
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+    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];
+    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);
+
+    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
+
+    if (bytesWritten < 0) {
+        return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+static Rappture::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 = Rappture::GetOpFromObj(interp, nDataSetGetVectorOps, dataSetGetVectorOps,
                                   Rappture::CMDSPEC_ARG2, objc, objv, 0);
     if (proc == NULL) {
@@ -1393,4 +1523,35 @@
         g_renderer->setUseCumulativeDataRange(false);
     } else {
+        Tcl_AppendResult(interp, "bad maprange option \"", value,
+                         "\": should be all, visible, or separate", (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;
+
+    size_t bytesWritten = SocketWrite(oss.str().c_str(), len);
+
+    if (bytesWritten < 0) {
         return TCL_ERROR;
     }
@@ -1416,12 +1577,14 @@
 
 static Rappture::CmdSpec dataSetOps[] = {
-    {"add",      1, DataSetAddOp, 6, 6, "name data follows nBytes"},
-    {"delete",   1, DataSetDeleteOp, 2, 3, "?name?"},
-    {"getvalue", 1, DataSetGetValueOp, 6, 7, "oper x y ?z? name"},
-    {"maprange", 1, DataSetMapRangeOp, 3, 3, "value"},
-    {"opacity",  1, 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?"}
+    {"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, 3, "value"},
+    {"names",     1, DataSetNamesOp, 2, 2, ""},
+    {"opacity",   1, 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);
@@ -1476,7 +1639,13 @@
     if (objc == 4) {
         const char *name = Tcl_GetString(objv[3]);
-        g_renderer->addGlyphs(name, shape);
-    } else {
-        g_renderer->addGlyphs("all", shape);
+        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;
@@ -1864,4 +2033,5 @@
 {
     std::vector<double> contourList;
+    double heightScale;
 
     int clistc;
@@ -1880,9 +2050,19 @@
     }
 
-    if (objc == 5) {
-        const char *name = Tcl_GetString(objv[4]);
-        g_renderer->addHeightMap(name, contourList);
-    } else {
-        g_renderer->addHeightMap("all", contourList);
+    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;
@@ -1894,12 +2074,22 @@
 {
     int numContours;
+    double heightScale;
     if (Tcl_GetIntFromObj(interp, objv[3], &numContours) != TCL_OK) {
         return TCL_ERROR;
     }
-    if (objc == 5) {
-        const char *name = Tcl_GetString(objv[4]);
-        g_renderer->addHeightMap(name, numContours);
-    } else {
-        g_renderer->addHeightMap("all", numContours);
+    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;
@@ -1907,6 +2097,6 @@
 
 static Rappture::CmdSpec heightmapAddOps[] = {
-    {"contourlist", 1, HeightMapAddContourListOp, 4, 5, "contourList ?dataSetName?"},
-    {"numcontours", 1, HeightMapAddNumContoursOp, 4, 5, "numContours ?dataSetName?"}
+    {"contourlist", 1, HeightMapAddContourListOp, 5, 6, "contourList heightscale ?dataSetName?"},
+    {"numcontours", 1, HeightMapAddNumContoursOp, 5, 6, "numContours heightscale ?dataSetName?"}
 };
 static int nHeightmapAddOps = NumCmdSpecs(heightmapAddOps);
@@ -1977,6 +2167,36 @@
 
 static int
-HeightMapContourVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
-                          Tcl_Obj *const *objv)
+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;
@@ -1986,7 +2206,24 @@
     if (objc == 4) {
         const char *name = Tcl_GetString(objv[3]);
-        g_renderer->setHeightMapContourVisibility(name, state);
-    } else {
-        g_renderer->setHeightMapContourVisibility("all", state);
+        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;
@@ -2089,4 +2326,22 @@
     } else {
         g_renderer->setHeightMapEdgeWidth("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;
@@ -2216,22 +2471,43 @@
 }
 
+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->setHeightMapWireframe(name, state);
+    } else {
+        g_renderer->setHeightMapWireframe("all", state);
+    }
+    return TCL_OK;
+}
+
 static Rappture::CmdSpec heightmapOps[] = {
-    {"add",          1, HeightMapAddOp, 4, 5, "oper value ?dataSetName?"},
-    {"colormap",     1, HeightMapColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"add",          1, HeightMapAddOp, 5, 6, "oper value ?dataSetName?"},
+    {"colormap",     2, HeightMapColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
+    {"contourlist",  2, 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?"},
     {"isolinecolor", 8, HeightMapContourLineColorOp, 5, 6, "r g b ?dataSetName?"},
-    {"isolines",     8, HeightMapContourVisibleOp, 3, 4, "bool ?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",          1, HeightMapPositionOp, 5, 6, "x y z ?dataSetName?"},
     {"scale",        1, HeightMapScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
+    {"surface",      2, HeightMapContourSurfaceVisibleOp, 3, 4, "bool ?dataSetName?"},
     {"visible",      2, HeightMapVisibleOp, 3, 4, "bool ?dataSetName?"},
-    {"volumeslice",  2, HeightMapVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"}
+    {"volumeslice",  2, HeightMapVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"},
+    {"wireframe",    1, HeightMapWireframeOp, 3, 4, "bool ?dataSetName?"}
 };
 static int nHeightmapOps = NumCmdSpecs(heightmapOps);
@@ -2310,7 +2586,13 @@
     if (objc == 3) {
         const char *name = Tcl_GetString(objv[2]);
-        g_renderer->addLIC(name);
-    } else {
-        g_renderer->addLIC("all");
+        if (!g_renderer->addLIC(name)) {
+            Tcl_AppendResult(interp, "Failed to create lic", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addLIC("all")) {
+            Tcl_AppendResult(interp, "Failed to create lic for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
     }
     return TCL_OK;
@@ -2574,7 +2856,13 @@
     if (objc == 3) {
         const char *name = Tcl_GetString(objv[2]);
-        g_renderer->addMolecule(name);
-    } else {
-        g_renderer->addMolecule("all");
+        if (!g_renderer->addMolecule(name)) {
+            Tcl_AppendResult(interp, "Failed to create molecule", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addMolecule("all")) {
+            Tcl_AppendResult(interp, "Failed to create molecule for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
     }
     return TCL_OK;
@@ -2889,7 +3177,13 @@
     if (objc == 3) {
         const char *name = Tcl_GetString(objv[2]);
-        g_renderer->addPolyData(name);
-    } else {
-        g_renderer->addPolyData("all");
+        if (!g_renderer->addPolyData(name)) {
+            Tcl_AppendResult(interp, "Failed to create polydata", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addPolyData("all")) {
+            Tcl_AppendResult(interp, "Failed to create polydata for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
     }
     return TCL_OK;
@@ -3031,4 +3325,21 @@
     } else {
         g_renderer->setPolyDataOrientation("all", quat);
+    }
+    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->setPolyDataPointSize(name, size);
+    } else {
+        g_renderer->setPolyDataPointSize("all", size);
     }
     return TCL_OK;
@@ -3117,5 +3428,6 @@
     {"opacity",   2, PolyDataOpacityOp, 3, 4, "value ?dataSetName?"},
     {"orient",    2, PolyDataOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
-    {"pos",       1, PolyDataPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"pos",       2, PolyDataPositionOp, 5, 6, "x y z ?dataSetName?"},
+    {"ptsize",    2, PolyDataPointSizeOp, 3, 4, "size ?dataSetName?"},
     {"scale",     1, PolyDataScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
     {"visible",   1, PolyDataVisibleOp, 3, 4, "bool ?dataSetName?"},
@@ -3144,7 +3456,13 @@
     if (objc == 3) {
         const char *name = Tcl_GetString(objv[2]);
-        g_renderer->addPseudoColor(name);
-    } else {
-        g_renderer->addPseudoColor("all");
+        if (!g_renderer->addPseudoColor(name)) {
+            Tcl_AppendResult(interp, "Failed to create pseudocolor", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addPseudoColor("all")) {
+            Tcl_AppendResult(interp, "Failed to create pseudocolor for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
     }
     return TCL_OK;
@@ -3529,7 +3847,13 @@
     if (objc == 3) {
         const char *name = Tcl_GetString(objv[2]);
-        g_renderer->addStreamlines(name);
-    } else {
-        g_renderer->addStreamlines("all");
+        if (!g_renderer->addStreamlines(name)) {
+            Tcl_AppendResult(interp, "Failed to create streamlines", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addStreamlines("all")) {
+            Tcl_AppendResult(interp, "Failed to create streamlines for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
     }
     return TCL_OK;
@@ -4103,7 +4427,13 @@
     if (objc == 3) {
         const char *name = Tcl_GetString(objv[2]);
-        g_renderer->addVolume(name);
-    } else {
-        g_renderer->addVolume("all");
+        if (!g_renderer->addVolume(name)) {
+            Tcl_AppendResult(interp, "Failed to create volume", (char*)NULL);
+            return TCL_ERROR;
+        }
+    } else {
+        if (!g_renderer->addVolume("all")) {
+            Tcl_AppendResult(interp, "Failed to create volume for one or more data sets", (char*)NULL);
+            return TCL_ERROR;
+        }
     }
     return TCL_OK;
Index: /trunk/packages/vizservers/vtkvis/protocol.txt
===================================================================
--- /trunk/packages/vizservers/vtkvis/protocol.txt	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/protocol.txt	(revision 2423)
@@ -47,7 +47,7 @@
 camera orient <quatW> <quatX> <quatY> <quatZ>
        Set scene orientation using a quaternion
-camera ortho <x> <y> <width> <height>
-       Supply world coordinate bounds of plot area for image camera mode
-       Data is assumed to lie in XY plane (z = 0)
+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
@@ -75,6 +75,8 @@
 dataset add <datasetName> data follows <nbytes>
 dataset delete <?datasetName?>
-dataset getvalue world <x> <y> <z> <datasetName>
-dataset getvalue pixel <x> <y> <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>
@@ -149,10 +151,11 @@
 glyphs wireframe <bool> <?datasetName?>
 
-heightmap add numcontours <n> <?dataSetName?>
+heightmap add numcontours <n> <heightScale> <?dataSetName?>
           Generate evenly spaced contours including range endpoints.  See also
           'dataset maprange' command.
-heightmap add contourlist <list> <?dataSetName?>
+heightmap add contourlist <list> <heightScale> <?dataSetName?>
 	  list = {isoval1 isoval2 isoval3...}
 heightmap colormap <colorMapName> <?dataSetName?>
+heightmap contourlist <list> <?dataSetName?>
 heightmap delete <?dataSetName?>
 heightmap edges <bool> <?dataSetName?>
@@ -164,12 +167,16 @@
 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 scale <sx> <sy> <sz> <?dataSetName?>
+heightmap surface <bool> <?dataSetName?>
+          Toggle rendering of colormapped surface (mountain plot or cutplane)
 heightmap visible <bool> <?dataSetName?>
 heightmap volumeslice axis ratio <?dataSetName?>
           For 3D data, select a slice along a principle axis of the volume. The
           ratio is [0,1]
+heightmap wireframe <bool> <?datasetName?>
 
 lic add <?datasetName?>
@@ -218,4 +225,5 @@
 polydata orient <qw> <qx> <qy> <qz> <?dataSetName?>
 polydata pos <x> <y> <z> <?dataSetName?>
+polydata ptsize <size> <?dataSetName?>
 polydata scale <sx> <sy> <sz> <?dataSetName?>
 polydata visible <bool> <?datasetName?>
@@ -301,13 +309,16 @@
 nv>image -type image -bytes <nbytes>
   <binary RGB data>
-nv>image -type image -bbox {x1 y1 x2 y2} -bytes <nbytes>
+nv>image -type image -bbox {x y w h} -bytes <nbytes>
   <binary RGB data>
-  Note: in this form, the bbox is the upper left and lower right screen corners
-  in world coordinates.  This form is currently used only if the camera mode is
-  set to 'image'.
+  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 coordinate 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> <nbytes>
   <binary RGB data>
-nv>dataset value world <x> <y> <z> <value>
-nv>dataset value pixel <x> <y> <value>
+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>
 
 ================================================================================
Index: /trunk/packages/vizservers/vtkvis/vtkRpCubeAxesActor2D.cpp
===================================================================
--- /trunk/packages/vizservers/vtkvis/vtkRpCubeAxesActor2D.cpp	(revision 2422)
+++ /trunk/packages/vizservers/vtkvis/vtkRpCubeAxesActor2D.cpp	(revision 2423)
@@ -419,8 +419,13 @@
   this->YAxis->SetFontFactor(AxisFontFactor);
   this->YAxis->SetProperty(this->GetProperty());
-
-  this->ZAxis->GetPositionCoordinate()->SetValue(zCoords[0], zCoords[1]);
-  this->ZAxis->GetPosition2Coordinate()->SetValue(zCoords[2], zCoords[3]);
-  this->ZAxis->SetRange(zRange[0], zRange[1]);
+  if ( this->YAxisVisibility ) {
+      this->ZAxis->GetPositionCoordinate()->SetValue(zCoords[0], zCoords[1]);
+      this->ZAxis->GetPosition2Coordinate()->SetValue(zCoords[2], zCoords[3]);
+      this->ZAxis->SetRange(zRange[0], zRange[1]);
+  } else {
+      this->ZAxis->GetPositionCoordinate()->SetValue(zCoords[2], zCoords[3]);
+      this->ZAxis->GetPosition2Coordinate()->SetValue(zCoords[0], zCoords[1]);
+      this->ZAxis->SetRange(zRange[1], zRange[0]);
+  }
   this->ZAxis->SetTitle(this->Labels[zAxes]);
   this->ZAxis->SetNumberOfLabels(this->NumberOfLabels);
