Index: trunk/packages/vizservers/nanovis/Command.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/Command.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/Command.cpp	(revision 1429)
@@ -141,5 +141,5 @@
 static Tcl_ObjCmdProc CameraCmd;
 static Tcl_ObjCmdProc CutplaneCmd;
-static Tcl_ObjCmdProc FlowCmd;
+extern Tcl_AppInitProc FlowCmdInitProc;
 static Tcl_ObjCmdProc GridCmd;
 static Tcl_ObjCmdProc LegendCmd;
@@ -1725,4 +1725,5 @@
 // ========================= VOLUME END ==================================
 
+#ifdef notdef
 // ============================= FLOW ==================================
 
@@ -1957,5 +1958,5 @@
         return TCL_ERROR;
     }
-    NanoVis::lic_on = state;
+    /* NanoVis::lic_on = state; */
     return TCL_OK;
 }
@@ -1973,4 +1974,5 @@
         return TCL_ERROR;
     }
+#ifdef notdef
     switch (axis) {
     case 0 :
@@ -1984,4 +1986,5 @@
         break;
     }
+#endif
     return TCL_OK;
 }
@@ -2053,5 +2056,5 @@
     }
 
-    NanoVis::particle_on = state;
+    /* NanoVis::particle_on = state; */
     return TCL_OK;
 }
@@ -2199,4 +2202,5 @@
 
 // ============================ FLOW END ==================================
+#endif
 
 static int
@@ -2832,10 +2836,13 @@
     Tcl_Interp *interp;
     interp = Tcl_CreateInterp();
+    /*
     Tcl_MakeSafe(interp);
-
+    */
     Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
     Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
     Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
-    Tcl_CreateObjCommand(interp, "flow",        FlowCmd,        NULL, NULL);
+    if (FlowCmdInitProc(interp) != TCL_OK) {
+	return NULL;
+    }
     Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
     Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
Index: trunk/packages/vizservers/nanovis/FlowCmd.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/FlowCmd.cpp	(revision 1429)
+++ trunk/packages/vizservers/nanovis/FlowCmd.cpp	(revision 1429)
@@ -0,0 +1,2017 @@
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <limits.h>
+#include <tcl.h>
+#include "Switch.h"
+#include <RpField1D.h>
+#include <RpFieldRect3D.h>
+#include <RpFieldPrism3D.h>
+#include <RpOutcome.h>
+#include <RpAVTranslate.h>
+#include "Trace.h"
+#include "TransferFunction.h"
+
+#include "nanovis.h"
+#include "Command.h"
+#include "CmdProc.h"
+#include "Nv.h"
+
+#include "NvLIC.h"
+
+#include "Unirect.h"
+#include "FlowCmd.h"
+
+#define RELPOS 0
+#define ABSPOS 1
+
+unsigned int FlowCmd::flags = 0;
+Tcl_HashTable FlowCmd::_flowTable;
+float FlowCmd::_magMin, FlowCmd::_magMax;
+float FlowCmd::_xMin,   FlowCmd::_xMax;
+float FlowCmd::_yMin,   FlowCmd::_yMax;
+float FlowCmd::_zMin,   FlowCmd::_zMax;
+float FlowCmd::_wMin,   FlowCmd::_wMax;
+float FlowCmd::_xOrigin, FlowCmd::_yOrigin, FlowCmd::_zOrigin;
+
+static Rappture::SwitchParseProc AxisSwitchProc;
+static Rappture::SwitchCustom axisSwitch = {
+    AxisSwitchProc, NULL, 0,
+};
+
+static Rappture::SwitchParseProc ColorSwitchProc;
+static Rappture::SwitchCustom colorSwitch = {
+    ColorSwitchProc, NULL, 0,
+};
+
+static Rappture::SwitchParseProc PointSwitchProc;
+static Rappture::SwitchCustom pointSwitch = {
+    PointSwitchProc, NULL, 0,
+};
+
+static Rappture::SwitchParseProc PositionSwitchProc;
+static Rappture::SwitchCustom positionSwitch = {
+    PositionSwitchProc, NULL, 0,
+};
+
+static Rappture::SwitchParseProc TransferFunctionSwitchProc;
+static Rappture::SwitchCustom transferFunctionSwitch = {
+    TransferFunctionSwitchProc, NULL, 0,
+};
+
+Rappture::SwitchSpec FlowCmd::_switches[] = {
+    {Rappture::SWITCH_BOOLEAN, "-slice", "boolean",
+	offsetof(FlowValues, sliceVisible), 0},
+    {Rappture::SWITCH_CUSTOM, "-axis", "axis",
+	offsetof(FlowValues, slicePos.axis), 0, 0, &axisSwitch},
+    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
+	offsetof(FlowValues, isHidden), 0},
+    {Rappture::SWITCH_CUSTOM, "-position", "number",
+	offsetof(FlowValues, slicePos), 0, 0, &positionSwitch},
+    {Rappture::SWITCH_CUSTOM, "-transferfunction", "name",
+        offsetof(FlowValues, tfPtr), 0, 0, &transferFunctionSwitch},
+    {Rappture::SWITCH_BOOLEAN, "-volume", "boolean",
+	offsetof(FlowValues, showVolume), 0},
+    {Rappture::SWITCH_BOOLEAN, "-outline", "boolean",
+	offsetof(FlowValues, showOutline), 0},
+    {Rappture::SWITCH_END}
+};
+
+Rappture::SwitchSpec FlowParticles::_switches[] = {
+    {Rappture::SWITCH_CUSTOM, "-axis", "string",
+     offsetof(FlowParticlesValues, position.axis), 0, 0, &axisSwitch},
+    {Rappture::SWITCH_CUSTOM, "-color", "{r g b a}",
+     offsetof(FlowParticlesValues, color), 0, 0,  &colorSwitch},
+    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
+     offsetof(FlowParticlesValues, isHidden), 0},
+    {Rappture::SWITCH_CUSTOM, "-position", "number",
+	offsetof(FlowValues, slicePos), 0, 0, &positionSwitch},
+    {Rappture::SWITCH_END}
+};
+
+Rappture::SwitchSpec FlowBox::_switches[] = {
+    {Rappture::SWITCH_CUSTOM, "-color", "{r g b a}",
+     offsetof(FlowBoxValues, color), 0, 0,  &colorSwitch},
+    {Rappture::SWITCH_CUSTOM, "-corner1", "{x y z}",
+     offsetof(FlowBoxValues, corner1), 0, 0, &pointSwitch},
+    {Rappture::SWITCH_CUSTOM, "-corner2", "{x y z}",
+     offsetof(FlowBoxValues, corner2), 0, 0, &pointSwitch},
+    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
+     offsetof(FlowBoxValues, isHidden), 0},
+    {Rappture::SWITCH_FLOAT, "-linewidth", "number",
+     offsetof(FlowBoxValues, lineWidth), 0},
+    {Rappture::SWITCH_END}
+};
+
+
+static Tcl_ObjCmdProc FlowInstObjCmd;
+static Tcl_CmdDeleteProc FlowInstDeleteProc;
+
+
+FlowParticles::FlowParticles(const char *name, Tcl_HashEntry *hPtr) 
+{
+    memset(this, 0, sizeof(FlowParticles));
+    _name = name;
+    _hashPtr = hPtr;
+    _sv.isHidden = false;
+    _sv.position.axis = 0;		/* X_AXIS */
+    _sv.position.value = 0.0f;	
+    _sv.position.flags = RELPOS;
+    _sv.color.r = _sv.color.b = _sv.color.g = _sv.color.a = 1.0f;
+    _rendererPtr = new NvParticleRenderer(NMESH, NMESH, 
+		/* Global nVidia Cg context */g_context);
+}
+
+void
+FlowParticles::Render(void) 
+{
+    fprintf(stderr, "rendering particles, position=%g\n",
+	    FlowCmd::GetPosition(&_sv.position));
+    _rendererPtr->setPos(FlowCmd::GetPosition(&_sv.position));
+    _rendererPtr->setAxis(_sv.position.axis);
+    assert(_rendererPtr->isActivated());
+    _rendererPtr->render();
+}
+
+void 
+FlowParticles::Configure(void) 
+{
+    _rendererPtr->setPos(_sv.position.value);
+    _rendererPtr->setColor(Vector4(_sv.color.r, _sv.color.g, _sv.color.b, 
+		_sv.color.a));
+    _rendererPtr->setAxis(_sv.position.axis);
+    if ((_sv.isHidden) && (_rendererPtr->isActivated())) {
+	_rendererPtr->deactivate();
+    } else if ((!_sv.isHidden) && (!_rendererPtr->isActivated())) {
+	_rendererPtr->activate();
+    }
+}
+
+FlowBox::FlowBox(const char *name, Tcl_HashEntry *hPtr) 
+{
+    _name = name;
+    _hashPtr = hPtr;
+    _sv.isHidden = false;
+    _sv.corner1.x = 0.0f;	
+    _sv.corner1.y = 0.0f;	
+    _sv.corner1.z = 0.0f;	
+    _sv.corner2.x = 1.0f;	
+    _sv.corner2.y = 1.0f;	
+    _sv.corner2.z = 1.0f;	
+    _sv.lineWidth = 1.2f;
+    _sv.color.r = _sv.color.b = _sv.color.g = _sv.color.a = 1.0f;
+
+}
+
+void 
+FlowBox::Render(Volume *volPtr)
+{
+    if ((_sv.corner1.x == _sv.corner2.x) ||
+	(_sv.corner1.y == _sv.corner2.y) ||
+	(_sv.corner1.z == _sv.corner2.z)) {
+	return;				
+    }
+    glColor4d(_sv.color.r, _sv.color.g, _sv.color.b, _sv.color.a);
+
+    glPushMatrix();
+
+    glEnable(GL_DEPTH_TEST);
+    glDisable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    
+    glPushMatrix();
+    Vector3 *originPtr = volPtr->get_location();
+    glTranslatef(originPtr->x, originPtr->y, originPtr->z);
+
+    double sx, sy, sz;
+    sx = 1.0;
+    sy = volPtr->height / (double)volPtr->width;
+    sz = volPtr->depth  / (double)volPtr->width;
+    glScaled(sx, sy, sz);
+
+    Vector3 min, max;
+
+    min = volPtr->getPhysicalBBoxMin();
+    max = volPtr->getPhysicalBBoxMax();
+
+    float x0, y0, z0, x1, y1, z1;
+    x0 = (_sv.corner1.x - min.x) / (max.x - min.x);
+    y0 = (_sv.corner1.y - min.y) / (max.y - min.y);
+    z0 = (_sv.corner1.z - min.z) / (max.z - min.z);
+    x1 = (_sv.corner2.x - min.x) / (max.x - min.x);
+    y1 = (_sv.corner2.y - min.y) / (max.y - min.y);
+    z1 = (_sv.corner2.z - min.z) / (max.z - min.z);
+    
+    glLineWidth(_sv.lineWidth);
+    glBegin(GL_LINE_LOOP); 
+    {
+	glVertex3d(x0, y0, z0);
+	glVertex3d(x1, y0, z0);
+	glVertex3d(x1, y1, z0);
+	glVertex3d(x0, y1, z0);
+    }
+    glEnd();
+    glBegin(GL_LINE_LOOP);
+    {
+	glVertex3d(x0, y0, z1);
+	glVertex3d(x1, y0, z1);
+	glVertex3d(x1, y1, z1);
+	glVertex3d(x0, y1, z1);
+    }
+    glEnd();
+    
+    glBegin(GL_LINE_LOOP);
+    {
+	glVertex3d(x0, y0, z0);
+	glVertex3d(x0, y0, z1);
+	glVertex3d(x0, y1, z1);
+	glVertex3d(x0, y1, z0);
+    }
+    glEnd();
+    
+    glBegin(GL_LINE_LOOP);
+    {
+	glVertex3d(x1, y0, z0);
+	glVertex3d(x1, y0, z1);
+	glVertex3d(x1, y1, z1);
+	glVertex3d(x1, y1, z0);
+    }
+    glEnd();
+
+    glPopMatrix();
+    assert(CheckGL(AT));
+    glPopMatrix();
+    assert(CheckGL(AT));
+
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_BLEND);
+    glEnable(GL_TEXTURE_2D);
+}
+
+
+FlowCmd::FlowCmd(Tcl_Interp *interp, const char *name, Tcl_HashEntry *hPtr)
+{
+    memset(this, 0, sizeof(FlowCmd));
+    _name = name;
+    _interp = interp;
+    _hashPtr = hPtr;
+    _volIndex = -1;			/* Indicates that no volume slot has
+					 * been allocated for this vector. */
+    _volPtr = NULL;
+    _cmdToken = Tcl_CreateObjCommand(interp, (char *)_name, 
+	(Tcl_ObjCmdProc *)FlowInstObjCmd, this, FlowInstDeleteProc);
+    Tcl_InitHashTable(&_particlesTable, TCL_STRING_KEYS);
+    Tcl_InitHashTable(&_boxTable, TCL_STRING_KEYS);
+}
+
+FlowCmd::~FlowCmd(void)
+{
+    Rappture::FreeSwitches(_switches, &_sv, 0);
+    if (_hashPtr != NULL) {
+	Tcl_DeleteHashEntry(_hashPtr);
+    }
+    if (_fieldPtr != NULL) {
+	delete _fieldPtr;
+    }
+    if (_dataPtr != NULL) {
+	delete _dataPtr;
+    }
+     if (_volPtr != NULL) {
+	delete _volPtr;
+	_volPtr = NULL;
+	NanoVis::volume[_volIndex] = NULL;
+	NanoVis::vol_renderer->remove_volume(_volIndex);
+    }
+
+    FlowBox *boxPtr;
+    FlowBoxIterator boxIter;
+    for (boxPtr = FirstBox(&boxIter); boxPtr != NULL; 
+	 boxPtr = NextBox(&boxIter)) {
+	boxPtr->disconnect();
+	delete boxPtr;
+    }	
+    FlowParticles *particlesPtr;
+    FlowParticlesIterator partIter;
+    for (particlesPtr = FirstParticles(&partIter); particlesPtr != NULL;
+	 particlesPtr = NextParticles(&partIter)) {
+	particlesPtr->disconnect();
+	delete particlesPtr;
+    }	
+    Tcl_DeleteHashTable(&_particlesTable);
+    Tcl_DeleteHashTable(&_boxTable);
+}
+
+void
+FlowCmd::ResetParticles(void)
+{
+    FlowParticlesIterator iter;
+    FlowParticles *particlesPtr;
+    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL;
+	 particlesPtr = NextParticles(&iter)) {
+	particlesPtr->Reset();
+    }
+}
+
+void
+FlowCmd::Advect(void)
+{
+    NvVectorField *fieldPtr;
+    fieldPtr = VectorField();
+    if (!fieldPtr->isActivated()) {
+	fieldPtr->activate();
+    }
+    FlowParticlesIterator iter;
+    FlowParticles *particlesPtr;
+    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL;
+	 particlesPtr = NextParticles(&iter)) {
+	if (particlesPtr->visible()) {
+	    particlesPtr->Advect();
+	}
+    }
+}
+
+void
+FlowCmd::Render(void)
+{
+    if (!_fieldPtr->isActivated()) {
+	_fieldPtr->activate();
+    }
+    _fieldPtr->render();
+    FlowParticlesIterator iter;
+    FlowParticles *particlesPtr;
+    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL; 
+	 particlesPtr = NextParticles(&iter)) {
+	if (particlesPtr->visible()) {
+	    particlesPtr->Render();
+	}
+    }
+    RenderBoxes();
+}
+
+void
+FlowCmd::Init(void) 
+{
+    Tcl_InitHashTable(&_flowTable, TCL_STRING_KEYS);
+}
+
+FlowCmd *
+FlowCmd::FirstFlow(FlowIterator *iterPtr) 
+{
+    iterPtr->hashPtr = Tcl_FirstHashEntry(&_flowTable, &iterPtr->hashSearch);
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    return (FlowCmd *)Tcl_GetHashValue(iterPtr->hashPtr);
+}
+
+FlowCmd *
+FlowCmd::NextFlow(FlowIterator *iterPtr) 
+{
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    iterPtr->hashPtr = Tcl_NextHashEntry(&iterPtr->hashSearch);
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    return (FlowCmd *)Tcl_GetHashValue(iterPtr->hashPtr);
+}
+
+
+int
+FlowCmd::GetFlow(Tcl_Interp *interp, Tcl_Obj *objPtr, FlowCmd **flowPtrPtr)
+{
+    Tcl_HashEntry *hPtr;
+    hPtr = Tcl_FindHashEntry(&_flowTable, Tcl_GetString(objPtr));
+    if (hPtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find a flow \"", 
+			     Tcl_GetString(objPtr), "\"", (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    *flowPtrPtr = (FlowCmd *)Tcl_GetHashValue(hPtr);
+    return TCL_OK;
+}
+
+int
+FlowCmd::CreateFlow(Tcl_Interp *interp, Tcl_Obj *objPtr)
+{
+    Tcl_HashEntry *hPtr;
+    int isNew;
+    const char *name;
+    name = Tcl_GetString(objPtr);
+    hPtr = Tcl_CreateHashEntry(&_flowTable, name, &isNew);
+    if (!isNew) {
+	Tcl_AppendResult(interp, "flow \"", name, "\" already exists.",
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_CmdInfo cmdInfo;
+    if (Tcl_GetCommandInfo(interp, name, &cmdInfo)) {
+	Tcl_AppendResult(interp, "an another command \"", name, 
+			 "\" already exists.", (char *)NULL);
+	return TCL_ERROR;
+    }	
+    FlowCmd *flowPtr;
+    name = Tcl_GetHashKey(&_flowTable, hPtr);
+    flowPtr = new FlowCmd(interp, name, hPtr);
+    if (flowPtr == NULL) {
+	Tcl_AppendResult(interp, "can't allocate a flow object \"", name, 
+			 "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    Tcl_SetHashValue(hPtr, flowPtr);
+    EventuallyRedraw(MAP_PENDING);
+    return TCL_OK;
+}
+
+void
+FlowCmd::DeleteFlows(Tcl_Interp *interp)
+{
+    FlowCmd *flowPtr;
+    FlowIterator iter;
+    for (flowPtr = FirstFlow(&iter); flowPtr != NULL; 
+	 flowPtr = NextFlow(&iter)) {
+	flowPtr->disconnect();		/* Don't disrupt the hash walk */
+	Tcl_DeleteCommand(interp, flowPtr->name());
+    }
+    Tcl_DeleteHashTable(&_flowTable);
+}
+
+bool
+FlowCmd::MapFlows(void)
+{
+    flags &= ~FlowCmd::MAP_PENDING;
+
+    _xMin = _yMin = _zMin = _wMin = _magMin = FLT_MAX;
+    _xMax = _yMax = _zMax = _wMax = _magMax = -FLT_MAX;
+
+    /* 
+     * Step 1.  Get the overall min and max magnitudes of all the 
+     *		flow vectors.
+     */
+    FlowCmd *flowPtr;
+    FlowIterator iter;
+    for (flowPtr = FirstFlow(&iter); flowPtr != NULL; 
+	 flowPtr = NextFlow(&iter)) {
+	double minMag, maxMag;
+	if (!flowPtr->isDataLoaded()) {
+	    continue;
+	}
+	flowPtr->GetMagRange(minMag, maxMag);
+	if (minMag < _magMin) {
+	    _magMin = minMag;
+	} 
+	if (maxMag > _magMax) {
+	    _magMax = maxMag;
+	}
+	Rappture::Unirect3d *dataPtr;
+	dataPtr = flowPtr->GetData();
+	if (dataPtr->xMin() < _xMin) {
+	    _xMin = dataPtr->xMin();
+	}
+	if (dataPtr->yMin() < _yMin) {
+	    _yMin = dataPtr->yMin();
+	}
+	if (dataPtr->zMin() < _zMin) {
+	    _zMin = dataPtr->zMin();
+	}
+	if (dataPtr->xMax() > _xMax) {
+	    _xMax = dataPtr->xMax();
+	}
+	if (dataPtr->yMax() > _yMax) {
+	    _yMax = dataPtr->yMax();
+	}
+	if (dataPtr->zMax() > _zMax) {
+	    _zMax = dataPtr->zMax();
+	}
+    }
+
+    /* 
+     * Step 2.  Generate the vector field from each data set. 
+     *		Delete the currently generated fields. 
+     */
+    for (flowPtr = FirstFlow(&iter); flowPtr != NULL; 
+	 flowPtr = NextFlow(&iter)) {
+	if (!flowPtr->isDataLoaded()) {
+	    continue;
+	}
+	flowPtr->InitVectorField();
+	if (!flowPtr->visible()) {
+	    continue;
+	}
+	if (!flowPtr->ScaleVectorField()) {
+	    return false;
+	}
+	flowPtr->InitializeParticles();
+	NanoVis::licRenderer->set_offset(flowPtr->GetPosition());
+    }
+    return true;
+}
+
+void
+FlowCmd::RenderFlows(void)
+{
+    if (!NanoVis::licRenderer->isActivated()) {
+	NanoVis::licRenderer->activate();
+    }
+    FlowCmd *flowPtr;
+    FlowIterator iter;
+    for (flowPtr = FirstFlow(&iter); flowPtr != NULL; 
+	 flowPtr = NextFlow(&iter)) {
+	if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
+	    flowPtr->Render();
+	}
+    }
+    flags &= ~FlowCmd::REDRAW_PENDING;
+}
+
+void
+FlowCmd::ResetFlows(void)
+{
+    if (!NanoVis::licRenderer->isActivated()) {
+	NanoVis::licRenderer->activate();
+    }
+    /*NanoVis::licRenderer->convolve();*/
+
+    FlowCmd *flowPtr;
+    FlowIterator iter;
+    for (flowPtr = FirstFlow(&iter); flowPtr != NULL; 
+	 flowPtr = NextFlow(&iter)) {
+	if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
+	    flowPtr->ResetParticles();
+	}
+    }
+}    
+
+void
+FlowCmd::AdvectFlows(void)
+{
+    if (!NanoVis::licRenderer->isActivated()) {
+	NanoVis::licRenderer->activate();
+    }
+    /*NanoVis::licRenderer->convolve();*/
+
+    FlowCmd *flowPtr;
+    FlowIterator iter;
+    for (flowPtr = FirstFlow(&iter); flowPtr != NULL; 
+	 flowPtr = NextFlow(&iter)) {
+	if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
+	    flowPtr->Advect();
+	}
+    }
+}    
+
+
+#ifdef notdef
+int
+FlowCmd::InitVectorField(Tcl_Interp *interp) 
+{
+    if (NanoVis::flowVisRenderer == NULL) {
+	Tcl_AppendResult(interp, "flowvis renderer is NULL", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (NanoVis::licRenderer == NULL) {
+	Tcl_AppendResult(interp, "LIC renderer is NULL", (char *)NULL);
+	return TCL_ERROR;
+    }
+    _fieldPtr->setVectorField(_volPtr->id, *(_volPtr->get_location()),
+	1.0f, _volPtr->height / (float)_volPtr->width,
+	_volPtr->depth  / (float)_volPtr->width,
+	_volPtr->wAxis.max());
+
+    NanoVis::initParticle();
+    NanoVis::licRenderer->setVectorField(
+	_volPtr->id,			/* Texture ID */
+	*(_volPtr->get_location()),	/* Origin */
+	1.0f / volPtr->aspect_ratio_width, /* X-axis scale. */
+	1.0f / volPtr->aspect_ratio_height, /* Y-axis scale. */
+	1.0f / volPtr->aspect_ratio_depth, /* Z-axis scale. */ 
+	volPtr->wAxis.max());		/* Maximum ???? */
+
+    NanoVis::licRenderer->set_offset(NanoVis::lic_slice_z);
+    return TCL_OK;
+}
+#endif
+
+int
+FlowCmd::CreateParticles(Tcl_Interp *interp, Tcl_Obj *objPtr)
+{
+    Tcl_HashEntry *hPtr;
+    int isNew;
+    const char *particlesName = Tcl_GetString(objPtr);
+    hPtr = Tcl_CreateHashEntry(&_particlesTable, particlesName, &isNew);
+    if (!isNew) {
+	Tcl_AppendResult(interp, "particle injection plane \"",
+			 particlesName, "\" already exists.", (char *)NULL);
+	return TCL_ERROR;
+    }
+    particlesName = Tcl_GetHashKey(&_particlesTable, hPtr);
+    FlowParticles *particlesPtr;
+    particlesPtr = new FlowParticles(particlesName, hPtr);
+    if (particlesPtr == NULL) {
+	Tcl_AppendResult(interp, "can't allocate particle injection plane",
+		(char *)NULL);
+	Tcl_DeleteHashEntry(hPtr);
+	return TCL_ERROR;
+    }
+    Tcl_SetHashValue(hPtr, particlesPtr);
+    return TCL_OK;
+}
+
+int
+FlowCmd::GetParticles(Tcl_Interp *interp, Tcl_Obj *objPtr, 
+		      FlowParticles **particlesPtrPtr)
+{
+    Tcl_HashEntry *hPtr;
+    hPtr = Tcl_FindHashEntry(&_particlesTable, Tcl_GetString(objPtr));
+    if (hPtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find a particle injection plane \"",
+			 Tcl_GetString(objPtr), "\"", (char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    *particlesPtrPtr = (FlowParticles *)Tcl_GetHashValue(hPtr);
+    return TCL_OK;
+}
+
+FlowParticles *
+FlowCmd::FirstParticles(FlowParticlesIterator *iterPtr) 
+{
+    iterPtr->hashPtr = Tcl_FirstHashEntry(&_particlesTable, 
+	&iterPtr->hashSearch);
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    return (FlowParticles *)Tcl_GetHashValue(iterPtr->hashPtr);
+}
+
+FlowParticles *
+FlowCmd::NextParticles(FlowParticlesIterator *iterPtr) 
+{
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    iterPtr->hashPtr = Tcl_NextHashEntry(&iterPtr->hashSearch);
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    return (FlowParticles *)Tcl_GetHashValue(iterPtr->hashPtr);
+}
+
+int
+FlowCmd::CreateBox(Tcl_Interp *interp, Tcl_Obj *objPtr)
+{
+    Tcl_HashEntry *hPtr;
+    int isNew;
+    hPtr = Tcl_CreateHashEntry(&_boxTable, Tcl_GetString(objPtr), &isNew);
+    if (!isNew) {
+	Tcl_AppendResult(interp, "box \"", Tcl_GetString(objPtr),
+		"\" already exists in flow \"", name(), "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    const char *boxName;
+    boxName = Tcl_GetHashKey(&_boxTable, hPtr);
+    FlowBox *boxPtr;
+    boxPtr = new FlowBox(boxName, hPtr);
+    if (boxPtr == NULL) {
+	Tcl_AppendResult(interp, "can't allocate box \"", boxName, "\"",
+			 (char *)NULL);
+	Tcl_DeleteHashEntry(hPtr);
+	return TCL_ERROR;
+    }
+    Tcl_SetHashValue(hPtr, boxPtr);
+    return TCL_OK;
+}
+
+int
+FlowCmd::GetBox(Tcl_Interp *interp, Tcl_Obj *objPtr, FlowBox **boxPtrPtr)
+{
+    Tcl_HashEntry *hPtr;
+    hPtr = Tcl_FindHashEntry(&_boxTable, Tcl_GetString(objPtr));
+    if (hPtr == NULL) {
+	if (interp != NULL) {
+	    Tcl_AppendResult(interp, "can't find a box \"", 
+		Tcl_GetString(objPtr), "\" in flow \"", name(), "\"", 
+		(char *)NULL);
+	}
+	return TCL_ERROR;
+    }
+    *boxPtrPtr = (FlowBox *)Tcl_GetHashValue(hPtr);
+    return TCL_OK;
+}
+
+FlowBox *
+FlowCmd::FirstBox(FlowBoxIterator *iterPtr) 
+{
+    iterPtr->hashPtr = Tcl_FirstHashEntry(&_boxTable, &iterPtr->hashSearch);
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    return (FlowBox *)Tcl_GetHashValue(iterPtr->hashPtr);
+}
+
+FlowBox *
+FlowCmd::NextBox(FlowBoxIterator *iterPtr) 
+{
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    iterPtr->hashPtr = Tcl_NextHashEntry(&iterPtr->hashSearch);
+    if (iterPtr->hashPtr == NULL) {
+	return NULL;
+    }
+    return (FlowBox *)Tcl_GetHashValue(iterPtr->hashPtr);
+}
+
+void
+FlowCmd::GetMagRange(double &min_mag, double &max_mag)
+{
+    const float *values = _dataPtr->values();
+    size_t nValues = _dataPtr->nValues();
+    max_mag = -1e21, min_mag = 1e21;
+    for (size_t i = 0; i < nValues; i += 3) {
+	double vx, vy, vz, vm;
+
+	vx = values[i];
+	vy = values[i+1];
+	vz = values[i+2];
+		    
+	vm = sqrt(vx*vx + vy*vy + vz*vz);
+	if (vm > max_mag) {
+	    max_mag = vm;
+	}
+	if (vm < min_mag) {
+	    min_mag = vm;
+	}
+    }
+}
+
+
+void
+FlowCmd::InitVectorField(void)
+{
+    if (_volPtr != NULL) {
+	delete _volPtr;
+	_volPtr = NULL;
+	NanoVis::volume[_volIndex] = NULL;
+	NanoVis::vol_renderer->remove_volume(_volIndex);
+    }
+    // Remove the associated vector field.
+    if (_fieldPtr != NULL) {
+	delete _fieldPtr;
+	_fieldPtr = NULL;
+    }
+    
+}
+
+void
+FlowCmd::InitializeParticles(void)
+{
+    FlowParticles *particlesPtr;
+    FlowParticlesIterator iter;
+    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL;
+	 particlesPtr = NextParticles(&iter)) {
+	particlesPtr->Initialize();
+    }	
+}
+
+bool
+FlowCmd::ScaleVectorField()
+{
+    if (_volPtr != NULL) {
+	delete _volPtr;
+	_volPtr = NULL;
+	NanoVis::volume[_volIndex] = NULL;
+	NanoVis::vol_renderer->remove_volume(_volIndex);
+    }
+    float *vdata;
+    vdata = GetScaledVector();
+    if (vdata == NULL) {
+	return false;
+    }
+    Volume *volPtr;
+    volPtr = MakeVolume(vdata);
+    delete [] vdata;
+    if (volPtr == NULL) {
+	return false;
+    }
+    _volPtr = volPtr;
+
+    _fieldPtr = new NvVectorField();
+    if (_fieldPtr == NULL) {
+	return false;
+    }
+
+    double width, height, depth;
+    width  = _xMax - _xMin;
+    height = _yMax - _yMin;
+    depth  = _zMax - _zMin;
+
+    Vector3 *locationPtr = _volPtr->get_location();
+    /*This is wrong. Need to compute origin. */
+    _xOrigin = locationPtr->x;
+    _yOrigin = locationPtr->y;
+    _zOrigin = locationPtr->z;
+
+    _fieldPtr->setVectorField(_volPtr, *locationPtr, 
+	1.0f, height / width, depth  / width, _magMax);
+
+    if (NanoVis::licRenderer != NULL) {
+        NanoVis::licRenderer->setVectorField(_volPtr->id, 
+		*locationPtr,
+		1.0f / _volPtr->aspect_ratio_width,
+		1.0f / _volPtr->aspect_ratio_height,
+		1.0f / _volPtr->aspect_ratio_depth,
+		_volPtr->wAxis.max());
+	SetCurrentPosition();
+	SetAxis();
+    }
+    FlowParticles *particlesPtr;
+    FlowParticlesIterator partIter;
+    for (particlesPtr = FirstParticles(&partIter); particlesPtr != NULL;
+	 particlesPtr = NextParticles(&partIter)) {
+	particlesPtr->SetVectorField(_volPtr);
+    }	
+    return true;
+}
+
+void
+FlowCmd::RenderBoxes(void)
+{
+    FlowBoxIterator iter;
+    FlowBox *boxPtr;
+    for (boxPtr = FirstBox(&iter); boxPtr != NULL; boxPtr = NextBox(&iter)) {
+	if (boxPtr->visible()) {
+	    boxPtr->Render(_volPtr);
+	}
+    }
+}
+
+float *
+FlowCmd::GetScaledVector(void)
+{
+    assert(_dataPtr->nComponents() == 3);
+    size_t n = _dataPtr->nValues() / _dataPtr->nComponents() * 4;
+    float *data = new float[n];
+    if (data == NULL) {
+	return NULL;
+    }
+    memset(data, 0, sizeof(float) * n);
+    float *destPtr = data;
+    const float *values = _dataPtr->values();
+    for (size_t iz=0; iz < _dataPtr->zNum(); iz++) {
+	for (size_t iy=0; iy < _dataPtr->yNum(); iy++) {
+	    for (size_t ix=0; ix < _dataPtr->xNum(); ix++) {
+		double vx, vy, vz, vm;
+		vx = values[0];
+		vy = values[1];
+		vz = values[2];
+		vm = sqrt(vx*vx + vy*vy + vz*vz);
+		destPtr[0] = vm / _magMax; 
+		destPtr[1] = vx /(2.0*_magMax) + 0.5; 
+		destPtr[2] = vy /(2.0*_magMax) + 0.5; 
+		destPtr[3] = vz /(2.0*_magMax) + 0.5; 
+		values += 3;
+		destPtr += 4;
+	    }
+	}
+    }
+    return data;
+}
+
+Volume *
+FlowCmd::MakeVolume(float *data)
+{
+    if (_volIndex < 0) {
+	_volIndex = NanoVis::n_volumes;
+    }
+    Volume *volPtr;
+    volPtr = NanoVis::load_volume(_volIndex, _dataPtr->xNum(), 
+	_dataPtr->yNum(), _dataPtr->zNum(), 4, data, _magMin, _magMax, 0);
+    volPtr->xAxis.SetRange(_dataPtr->xMin(), _dataPtr->xMax());
+    volPtr->yAxis.SetRange(_dataPtr->yMin(), _dataPtr->yMax());
+    volPtr->zAxis.SetRange(_dataPtr->zMin(), _dataPtr->zMax());
+    volPtr->wAxis.SetRange(_magMin, _magMax);
+
+    /*volPtr->update_pending = false;*/
+    Vector3 physicalMin(_xMin, _yMin, _zMin);
+    Vector3 physicalMax(_xMax, _yMax, _zMax);
+    volPtr->setPhysicalBBox(physicalMin, physicalMax);
+
+    volPtr->set_n_slice(256 - _volIndex);
+    // volPtr->set_n_slice(512- _volIndex);
+    volPtr->disable_cutplane(0);
+    volPtr->disable_cutplane(1);
+    volPtr->disable_cutplane(2);
+
+    TransferFunction *tfPtr;
+    tfPtr = _sv.tfPtr;
+    if (tfPtr == NULL) {
+	tfPtr = NanoVis::get_transfunc("default");
+    }
+    NanoVis::vol_renderer->add_volume(volPtr, tfPtr);
+    if (_sv.showVolume) {
+	volPtr->enable_data();
+    } else {
+	volPtr->disable_data();
+    }
+    if (_sv.showOutline) {
+	volPtr->enable_outline();
+    } else {
+	volPtr->disable_outline();
+    }
+    float dx0 = -0.5;
+    float dy0 = -0.5*volPtr->height/volPtr->width;
+    float dz0 = -0.5*volPtr->depth/volPtr->width;
+    volPtr->move(Vector3(dx0, dy0, dz0));
+    return volPtr;
+}
+
+static int
+FlowDataFileOp(ClientData clientData, Tcl_Interp *interp, int objc,
+	       Tcl_Obj *const *objv)
+{
+    Rappture::Outcome result;
+
+    const char *fileName;
+    fileName = Tcl_GetString(objv[3]);
+    Trace("Flow loading data from file %s\n", fileName);
+
+    int extents;
+    if (Tcl_GetIntFromObj(interp, objv[4], &extents) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if ((extents < 1) || (extents > 4)) {
+	Tcl_AppendResult(interp, "bad extents value \"", Tcl_GetString(objv[4]),
+			 "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    Rappture::Buffer buf;
+    if (!buf.load(result, fileName)) {
+	Tcl_AppendResult(interp, "can't load data from \"", fileName, "\": ",
+			 result.remark(), (char *)NULL);
+	return TCL_ERROR;
+    }
+
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    if (strncmp(buf.bytes(), "<DX>", 4) == 0) {
+	Rappture::Unirect3d *dataPtr;
+
+	dataPtr = new Rappture::Unirect3d(extents);
+	if (!dataPtr->ReadVectorDataFromDx(result, buf.size(), 
+		(char *)buf.bytes())) {
+	    Tcl_AppendResult(interp, result.remark(), (char *)NULL);
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	flowPtr->SetData(dataPtr);
+    } else if (strncmp(buf.bytes(), "<unirect3d>", 4) == 0) {
+	Rappture::Unirect3d *dataPtr;
+	Tcl_CmdInfo cmdInfo;
+
+	/* Set the clientdata field of the unirect3d command to contain
+	 * the local data structure. */
+	if (!Tcl_GetCommandInfo(interp, "unirect3d", &cmdInfo)) {
+	    return TCL_ERROR;
+	}
+	dataPtr = new Rappture::Unirect3d(extents);
+	cmdInfo.objClientData = (ClientData)dataPtr;	
+	Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
+	if (Tcl_Eval(interp, (const char *)buf.bytes()) != TCL_OK) {
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	if (!dataPtr->isInitialized()) {
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	flowPtr->SetData(dataPtr);
+    } else {
+	Rappture::Unirect3d *dataPtr;
+
+	dataPtr = new Rappture::Unirect3d(extents);
+	if (!dataPtr->ReadVectorDataFromDx(result, buf.size(), 
+					   (char *)buf.bytes())) {
+	    Tcl_AppendResult(interp, result.remark(), (char *)NULL);
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	flowPtr->SetData(dataPtr);
+    }
+    FlowCmd::EventuallyRedraw(FlowCmd::MAP_PENDING);
+    return TCL_OK;
+}
+
+static int
+FlowDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
+                    Tcl_Obj *const *objv)
+{
+    Rappture::Outcome result;
+
+    Trace("Flow Data Loading\n");
+
+    int nBytes;
+    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (nBytes <= 0) {
+	Tcl_AppendResult(interp, "bad # bytes request \"", 
+		Tcl_GetString(objv[3]), "\" for \"data follows\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    int extents;
+    if (Tcl_GetIntFromObj(interp, objv[4], &extents) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    Rappture::Buffer buf;
+    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    if ((buf.size() > 4) && (strncmp(buf.bytes(), "<DX>", 4) == 0)) {
+	Rappture::Unirect3d *dataPtr;
+
+	dataPtr = new Rappture::Unirect3d(extents);
+	if (!dataPtr->ReadVectorDataFromDx(result, buf.size(), 
+		(char *)buf.bytes())) {
+	    Tcl_AppendResult(interp, result.remark(), (char *)NULL);
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	flowPtr->SetData(dataPtr);
+    } else if ((buf.size() > 4) && 
+	       (strncmp(buf.bytes(), "<unirect3d>", 4) == 0)) {
+	Rappture::Unirect3d *dataPtr;
+	Tcl_CmdInfo cmdInfo;
+
+	/* Set the clientdata field of the unirect3d command to contain
+	 * the local data structure. */
+	dataPtr = new Rappture::Unirect3d(extents);
+	if (!Tcl_GetCommandInfo(interp, "unirect3d", &cmdInfo)) {
+	    return TCL_ERROR;
+	}
+	cmdInfo.objClientData = (ClientData)dataPtr;	
+	Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
+	if (Tcl_Eval(interp, (const char *)buf.bytes()) != TCL_OK) {
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	if (!dataPtr->isInitialized()) {
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	flowPtr->SetData(dataPtr);
+    } else {
+	Tcl_AppendResult(interp, "unknown data header \"", /*buf.bytes(), "\"",*/
+			 (char *)NULL);
+	Rappture::Unirect3d *dataPtr;
+
+	dataPtr = new Rappture::Unirect3d(extents);
+	if (!dataPtr->ReadVectorDataFromDx(result, buf.size(), 
+					   (char *)buf.bytes())) {
+	    Tcl_AppendResult(interp, result.remark(), (char *)NULL);
+	    delete dataPtr;
+	    return TCL_ERROR;
+	}
+	flowPtr->SetData(dataPtr);
+    }
+    FlowCmd::EventuallyRedraw(FlowCmd::MAP_PENDING);
+    return TCL_OK;
+}
+
+static Rappture::CmdSpec flowDataOps[] = {
+    {"file",    2, FlowDataFileOp,    5, 5, "fileName extents",},
+    {"follows", 2, FlowDataFollowsOp, 5, 5, "size extents",},
+};
+static int nFlowDataOps = NumCmdSpecs(flowDataOps);
+
+static int
+FlowDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
+	   Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = Rappture::GetOpFromObj(interp, nFlowDataOps, flowDataOps,
+                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+        return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+
+static int
+FlowVectorIdOp(ClientData clientData, Tcl_Interp *interp, int objc,
+             Tcl_Obj *const *objv)
+{
+#ifdef notdef
+    Volume *volPtr;
+    if (GetVolumeFromObj(interp, objv[2], &volPtr) != TCL_OK) {
+        return TCL_ERROR;
+    }
+    if (NanoVis::flowVisRenderer != NULL) {
+        NanoVis::flowVisRenderer->setVectorField(volPtr->id, 
+            *(volPtr->get_location()),
+            1.0f,
+            volPtr->height / (float)volPtr->width,
+            volPtr->depth  / (float)volPtr->width,
+            volPtr->wAxis.max());
+        NanoVis::initParticle();
+    }
+    if (NanoVis::licRenderer != NULL) {
+        NanoVis::licRenderer->setVectorField(volPtr->id,
+            *(volPtr->get_location()),
+            1.0f / volPtr->aspect_ratio_width,
+            1.0f / volPtr->aspect_ratio_height,
+            1.0f / volPtr->aspect_ratio_depth,
+            volPtr->wAxis.max());
+        NanoVis::licRenderer->set_offset(NanoVis::lic_slice_z);
+    }
+#endif
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisSwitchProc --
+ *
+ *	Convert a Tcl_Obj representing the label of a child node into its
+ *	integer node id.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+AxisSwitchProc(
+    ClientData clientData,	/* Flag indicating if the node is considered
+				 * before or after the insertion position. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    const char *switchName,	/* Not used. */
+    Tcl_Obj *objPtr,		/* String representation */
+    char *record,		/* Structure record */
+    int offset,			/* Not used. */
+    int flags)			/* Not used. */
+{
+    const char *string = Tcl_GetString(objPtr);
+    if (string[1] == '\0') {
+	FlowCmd::SliceAxis *axisPtr = (FlowCmd::SliceAxis *)(record + offset);
+        char c;
+        c = tolower((unsigned char)string[0]);
+        if (c == 'x') {
+            *axisPtr = FlowCmd::AXIS_X;
+            return TCL_OK;
+        } else if (c == 'y') {
+            *axisPtr = FlowCmd::AXIS_Y;
+            return TCL_OK;
+        } else if (c == 'z') {
+            *axisPtr = FlowCmd::AXIS_Z;
+            return TCL_OK;
+        }
+        /*FALLTHRU*/
+    }
+    Tcl_AppendResult(interp, "bad axis \"", string,
+                     "\": should be x, y, or z", (char*)NULL);
+    return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * ColorSwitchProc --
+ *
+ *	Convert a Tcl_Obj representing the label of a list of four color 
+ *	components in to a RGBA color value.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+ColorSwitchProc(
+    ClientData clientData,	/* Flag indicating if the node is considered
+				 * before or after the insertion position. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    const char *switchName,	/* Not used. */
+    Tcl_Obj *objPtr,		/* String representation */
+    char *record,		/* Structure record */
+    int offset,			/* Not used. */
+    int flags)			/* Not used. */
+{
+    Tcl_Obj **objv;
+    int objc;
+    FlowColor *colorPtr = (FlowColor *)(record + offset);
+
+    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if ((objc < 3) || (objc > 4)) {
+	Tcl_AppendResult(interp, "wrong # of elements in color definition",
+			 (char *)NULL);
+	return TCL_ERROR;
+    }
+    float values[4];
+    int i;
+    values[3] = 1.0f;
+    for (i = 0; i < objc; i++) {
+	float value;
+
+	if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	if ((value < 0.0) || (value > 1.0)) {
+	    Tcl_AppendResult(interp, "bad component value in \"",
+		Tcl_GetString(objPtr), "\": color values must be [0..1]",
+		(char *)NULL);
+	    return TCL_ERROR;
+	}
+	values[i] = value;
+    }	    
+    colorPtr->r = values[0];
+    colorPtr->g = values[1];
+    colorPtr->b = values[2];
+    colorPtr->a = values[3];
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PointSwitchProc --
+ *
+ *	Convert a Tcl_Obj representing the a 3-D coordinate into
+ *	a point.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PointSwitchProc(
+    ClientData clientData,	/* Flag indicating if the node is considered
+				 * before or after the insertion position. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    const char *switchName,	/* Not used. */
+    Tcl_Obj *objPtr,		/* String representation */
+    char *record,		/* Structure record */
+    int offset,			/* Not used. */
+    int flags)			/* Not used. */
+{
+    FlowPoint *pointPtr = (FlowPoint *)(record + offset);
+    int objc;
+    Tcl_Obj **objv;
+
+    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (objc != 3) {
+	Tcl_AppendResult(interp, "wrong # of elements for box coordinates: "
+			 " should be \"x y z\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    float values[3];
+    int i;
+    for (i = 0; i < objc; i++) {
+	float value;
+
+	if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	values[i] = value;
+    }	    
+    pointPtr->x = values[0];
+    pointPtr->y = values[1];
+    pointPtr->z = values[2];
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * PositionSwitchProc --
+ *
+ *	Convert a Tcl_Obj representing the a 3-D coordinate into
+ *	a point.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+PositionSwitchProc(
+    ClientData clientData,	/* Flag indicating if the node is considered
+				 * before or after the insertion position. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    const char *switchName,	/* Not used. */
+    Tcl_Obj *objPtr,		/* String representation */
+    char *record,		/* Structure record */
+    int offset,			/* Not used. */
+    int flags)			/* Not used. */
+{
+    FlowPosition *posPtr = (FlowPosition *)(record + offset);
+    const char *string;
+    char *p;
+
+    string = Tcl_GetString(objPtr);
+    p = strrchr(string, '%');
+    if (p == NULL) {
+	float value;
+
+	if (GetFloatFromObj(interp, objPtr, &value) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	posPtr->value = value;
+	fprintf(stderr, "got absolute value %s\n", string);
+	posPtr->flags = ABSPOS;
+    } else {
+	double value;
+
+	*p = '\0';
+	if (Tcl_GetDouble(interp, string, &value) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	posPtr->value = (float)value * 0.01;
+	posPtr->flags = RELPOS;
+    }
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TransferFunctionSwitchProc --
+ *
+ *	Convert a Tcl_Obj representing the transfer function into a
+ *	TransferFunction pointer.  The transfer function must have been
+ *	previously defined.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+TransferFunctionSwitchProc(
+    ClientData clientData,	/* Flag indicating if the node is considered
+				 * before or after the insertion position. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    const char *switchName,	/* Not used. */
+    Tcl_Obj *objPtr,		/* String representation */
+    char *record,		/* Structure record */
+    int offset,			/* Not used. */
+    int flags)			/* Not used. */
+{
+    TransferFunction **funcPtrPtr = (TransferFunction **)(record + offset);
+    TransferFunction *funcPtr;
+    funcPtr = NanoVis::get_transfunc(Tcl_GetString(objPtr));
+    if (funcPtr == NULL) {
+        Tcl_AppendResult(interp, "transfer function \"", Tcl_GetString(objPtr),
+                         "\" is not defined", (char*)NULL);
+        return TCL_ERROR;
+    }
+    *funcPtrPtr = funcPtr;
+    return TCL_OK;
+}
+
+static int
+FlowConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    if (flowPtr->ParseSwitches(interp, objc - 2, objv + 2) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    FlowCmd::EventuallyRedraw();
+    return TCL_OK;
+}
+
+static int
+FlowParticlesAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		   Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    if (flowPtr->CreateParticles(interp, objv[3]) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    FlowParticles *particlesPtr;
+    if (flowPtr->GetParticles(interp, objv[3], &particlesPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
+	delete particlesPtr;
+	return TCL_ERROR;
+    }
+    particlesPtr->Configure();
+    FlowCmd::EventuallyRedraw(FlowCmd::MAP_PENDING);
+    Tcl_SetObjResult(interp, objv[3]);
+    return TCL_OK;
+}
+
+static int
+FlowParticlesConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
+			 Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    FlowParticles *particlesPtr;
+    if (flowPtr->GetParticles(interp, objv[3], &particlesPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    particlesPtr->Configure();
+    FlowCmd::EventuallyRedraw();
+    return TCL_OK;
+}
+
+static int
+FlowParticlesDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		      Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    int i;
+    for (i = 3; i < objc; i++) {
+	FlowParticles *particlesPtr;
+
+	if (flowPtr->GetParticles(NULL, objv[i], &particlesPtr) == TCL_OK) {
+	    delete particlesPtr;
+	}
+    }
+    FlowCmd::EventuallyRedraw(FlowCmd::MAP_PENDING);
+    return TCL_OK;
+}
+
+static int
+FlowParticlesNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		     Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    Tcl_Obj *listObjPtr;
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    FlowParticlesIterator iter;
+    FlowParticles *particlesPtr;
+    for (particlesPtr = flowPtr->FirstParticles(&iter); particlesPtr != NULL; 
+	 particlesPtr = flowPtr->NextParticles(&iter)) {
+	Tcl_Obj *objPtr;
+
+	objPtr = Tcl_NewStringObj(particlesPtr->name(), -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowParticlesObjCmd --
+ *
+ * 	This procedure is invoked to process commands on behalf of the flow
+ * 	object.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * $flow particles oper $name 
+ *---------------------------------------------------------------------------
+ */
+static Rappture::CmdSpec flowParticlesOps[] = {
+    {"add",        1, FlowParticlesAddOp,        4, 0, "name ?switches?",},
+    {"configure",  1, FlowParticlesConfigureOp,  4, 0, "name ?switches?",},
+    {"delete",	   1, FlowParticlesDeleteOp,     4, 0, "?name...?"},
+    {"names",      1, FlowParticlesNamesOp,      3, 4, "?pattern?"},
+};
+
+static int nFlowParticlesOps = NumCmdSpecs(flowParticlesOps);
+
+static int
+FlowParticlesOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+	       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+    proc = Rappture::GetOpFromObj(interp, nFlowParticlesOps, flowParticlesOps, 
+	Rappture::CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    Tcl_Preserve(flowPtr);
+    int result;
+    result = (*proc) (clientData, interp, objc, objv);
+    Tcl_Release(flowPtr);
+    return result;
+}
+
+static int
+FlowBoxAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
+	       Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    if (flowPtr->CreateBox(interp, objv[3]) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    FlowBox *boxPtr;
+    if (flowPtr->GetBox(interp, objv[3], &boxPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (boxPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
+	delete boxPtr;
+	return TCL_ERROR;
+    }
+    FlowCmd::EventuallyRedraw();
+    Tcl_SetObjResult(interp, objv[3]);
+    return TCL_OK;
+}
+
+static int
+FlowBoxDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		   Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    int i;
+    for (i = 3; i < objc; i++) {
+	FlowBox *boxPtr;
+
+	if (flowPtr->GetBox(NULL, objv[i], &boxPtr) == TCL_OK) {
+	    delete boxPtr;
+	}
+    }
+    FlowCmd::EventuallyRedraw();
+    return TCL_OK;
+}
+
+static int
+FlowBoxNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
+             Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    Tcl_Obj *listObjPtr;
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    FlowBoxIterator iter;
+    FlowBox *boxPtr;
+    for (boxPtr = flowPtr->FirstBox(&iter); boxPtr != NULL; 
+	 boxPtr = flowPtr->NextBox(&iter)) {
+	Tcl_Obj *objPtr;
+
+	objPtr = Tcl_NewStringObj(boxPtr->name(), -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+static int
+FlowBoxConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
+			Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    FlowBox *boxPtr;
+    if (flowPtr->GetBox(interp, objv[3], &boxPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (boxPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    FlowCmd::EventuallyRedraw();
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowBoxOp--
+ *
+ * 	This procedure is invoked to process commands on behalf of the flow
+ * 	object.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Rappture::CmdSpec flowBoxOps[] = {
+    {"add",        1, FlowBoxAddOp,        4, 0, "name ?switches?",},
+    {"configure",  1, FlowBoxConfigureOp,  4, 0, "name ?switches?",},
+    {"delete",	   1, FlowBoxDeleteOp,     3, 0, "?name...?"},
+    {"names",      1, FlowBoxNamesOp,      3, 0, "?pattern?"},
+};
+
+static int nFlowBoxOps = NumCmdSpecs(flowBoxOps);
+
+static int
+FlowBoxOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+	       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+    proc = Rappture::GetOpFromObj(interp, nFlowBoxOps, flowBoxOps, 
+	Rappture::CMDSPEC_ARG2, objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    Tcl_Preserve(flowPtr);
+    int result;
+    result = (*proc) (clientData, interp, objc, objv);
+    Tcl_Release(flowPtr);
+    return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowInstObjCmd --
+ *
+ * 	This procedure is invoked to process commands on behalf of the flow
+ * 	object.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ *---------------------------------------------------------------------------
+ */
+static Rappture::CmdSpec flowInstOps[] = {
+    {"box",         1, FlowBoxOp,        2, 0, "oper ?args?"},
+    {"configure",   1, FlowConfigureOp,  2, 0, "?switches?"},
+    {"data",	    1, FlowDataOp,       2, 0, "oper ?args?"},
+    {"particles",   1, FlowParticlesOp,  2, 0, "oper ?args?"}
+};
+static int nFlowInstOps = NumCmdSpecs(flowInstOps);
+
+static int
+FlowInstObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
+	       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+    proc = Rappture::GetOpFromObj(interp, nFlowInstOps, flowInstOps, 
+	Rappture::CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    assert(CheckGL(AT));
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    Tcl_Preserve(flowPtr);
+    int result;
+    result = (*proc) (clientData, interp, objc, objv);
+    Tcl_Release(flowPtr);
+    return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowInstDeleteProc --
+ *
+ *	Deletes the command associated with the tree.  This is called only
+ *	when the command associated with the tree is destroyed.
+ *
+ * Results:
+ *	None.
+ *
+ *---------------------------------------------------------------------------
+ */
+static void
+FlowInstDeleteProc(ClientData clientData)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    delete flowPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowAddOp --
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FlowAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
+	  Tcl_Obj *const *objv)
+{
+    if (FlowCmd::CreateFlow(interp, objv[2]) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    FlowCmd *flowPtr;
+    if (FlowCmd::GetFlow(interp, objv[2], &flowPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (flowPtr->ParseSwitches(interp, objc - 3, objv + 3) != TCL_OK) {
+	Tcl_DeleteCommand(interp, flowPtr->name());
+	return TCL_ERROR;
+    }
+    Tcl_SetObjResult(interp, objv[2]);
+    FlowCmd::EventuallyRedraw();
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowDeleteOp --
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FlowDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
+	     Tcl_Obj *const *objv)
+{
+    int i;
+
+    for (i = 2; i < objc; i++) {
+	FlowCmd *flowPtr;
+
+	if (FlowCmd::GetFlow(interp, objv[i], &flowPtr) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	Tcl_DeleteCommand(interp, flowPtr->name());
+    }
+    FlowCmd::EventuallyRedraw(FlowCmd::MAP_PENDING);
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowNamesOp --
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+FlowNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
+	    Tcl_Obj *const *objv)
+{
+    Tcl_Obj *listObjPtr;
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    FlowCmd *flowPtr;
+    FlowIterator iter;
+    for (flowPtr = FlowCmd::FirstFlow(&iter); flowPtr != NULL; 
+	 flowPtr = FlowCmd::NextFlow(&iter)) {
+	Tcl_Obj *objPtr;
+
+	objPtr = Tcl_NewStringObj(flowPtr->name(), -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+static int
+FlowNextOp(ClientData clientData, Tcl_Interp *interp, int objc,
+             Tcl_Obj *const *objv)
+{
+    assert(NanoVis::licRenderer != NULL);
+    if (!NanoVis::licRenderer->isActivated()) {
+        NanoVis::licRenderer->activate();
+    }
+    Trace("sending flow playback frame\n");
+    // Generate the latest frame and send it back to the client
+    if (FlowCmd::flags & FlowCmd::MAP_PENDING) {
+	FlowCmd::MapFlows();
+    }
+    FlowCmd::EventuallyRedraw();
+    NanoVis::licRenderer->convolve();
+    FlowCmd::AdvectFlows();
+    NanoVis::offscreen_buffer_capture();  //enable offscreen render
+    NanoVis::display();
+    NanoVis::read_screen();
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    static int frame_count = 0;
+    NanoVis::bmp_write_to_file(++frame_count, ".");
+    Trace("FLOW end\n");
+    return TCL_OK;
+}
+
+static int
+FlowResetOp(ClientData clientData, Tcl_Interp *interp, int objc,
+             Tcl_Obj *const *objv)
+{
+    FlowCmd::ResetFlows();
+    NanoVis::licRenderer->reset();
+    return TCL_OK;
+}
+
+static int
+FlowVideoOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+	    Tcl_Obj *const *objv)
+{
+    int width, height;		// Resolution of video.
+    int numFrames;		// Total number of frames.
+    float frameRate;		// Frame rate of the video.
+    float bitRate;		// Bit rate of the vide.
+
+    if ((Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK) ||
+	(Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) ||
+	(Tcl_GetIntFromObj(interp, objv[4], &numFrames) != TCL_OK) ||
+	(GetFloatFromObj(interp, objv[5], &frameRate) != TCL_OK) ||
+	(GetFloatFromObj(interp, objv[6], &bitRate) != TCL_OK)) {
+        return TCL_ERROR;
+    }
+    if ((width<0) || (width>SHRT_MAX) || (height<0) || (height>SHRT_MAX)) {
+	Tcl_AppendResult(interp, "bad dimensions for video", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((frameRate < 0.0f) || (frameRate > 30.0f)) {
+	Tcl_AppendResult(interp, "bad frame rate \"", Tcl_GetString(objv[5]),
+			 "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if ((bitRate < 0.0f) || (frameRate > 30.0f)) {
+	Tcl_AppendResult(interp, "bad bit rate \"", Tcl_GetString(objv[6]),
+			 "\"", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (NanoVis::licRenderer == NULL) {
+	Tcl_AppendResult(interp, "no lic renderer.", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (NanoVis::flowVisRenderer == NULL) {
+	Tcl_AppendResult(interp, "no flow renderer.", (char *)NULL);
+	return TCL_ERROR;
+    }
+    NanoVis::licRenderer->activate();
+    NanoVis::flowVisRenderer->activate();
+
+    // Save the old dimensions of the offscreen buffer.
+    int oldWidth, oldHeight;
+    oldWidth = NanoVis::win_width;
+    oldHeight = NanoVis::win_height;
+
+    if ((width != oldWidth) || (height != oldHeight)) {
+	// Resize to the requested size.
+	NanoVis::resize_offscreen_buffer(width, height);
+    }
+
+    char fileName[128];
+    sprintf(fileName,"/tmp/flow%d.mpeg", getpid());
+
+    Trace("FLOW started\n");
+
+    Rappture::Outcome result;
+    Rappture::AVTranslate movie(width, height, frameRate, bitRate);
+
+    int pad = 0;
+    if ((3*NanoVis::win_width) % 4 > 0) {
+        pad = 4 - ((3*NanoVis::win_width) % 4);
+    }
+
+    movie.init(result, fileName);
+
+    for (int i = 0; i < numFrames; i++) {
+        // Generate the latest frame and send it back to the client
+	NanoVis::licRenderer->convolve();
+	FlowCmd::AdvectFlows();
+	FlowCmd::RenderFlows();
+        NanoVis::offscreen_buffer_capture();  //enable offscreen render
+        NanoVis::display();
+
+        NanoVis::read_screen();
+        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+
+        // This is done before bmp_write_to_file because bmp_write_to_file
+        // turns rgb data to bgr
+        movie.append(result, NanoVis::screen_buffer, pad);
+        // NanoVis::bmp_write_to_file(frame_count, fileName);
+    }
+
+    movie.done(result);
+    Trace("FLOW end\n");
+
+    if (NanoVis::licRenderer) {
+        NanoVis::licRenderer->deactivate();
+    }
+    if (NanoVis::flowVisRenderer) {
+        NanoVis::flowVisRenderer->deactivate();
+    }
+    NanoVis::initParticle();
+
+    // FIXME: find a way to get the data from the movie object as a void*
+    Rappture::Buffer data;
+    if (!data.load(result, fileName)) {
+        Tcl_AppendResult(interp, "can't load data from temporary movie file \"",
+		fileName, "\": ", result.remark(), (char *)NULL);
+	return TCL_ERROR;
+    }
+    // Build the command string for the client.
+    char command[200];
+    sprintf(command,"nv>image -bytes %lu -type movie -token token\n", 
+	    (unsigned long)data.size());
+
+    NanoVis::sendDataToClient(command, data.bytes(), data.size());
+    if (unlink(fileName) != 0) {
+        Tcl_AppendResult(interp, "can't unlink temporary movie file \"",
+		fileName, "\": ", Tcl_PosixError(interp), (char *)NULL);
+	return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowObjCmd --
+ *
+ *---------------------------------------------------------------------------
+ */
+static Rappture::CmdSpec flowCmdOps[] = {
+    {"add",      1, FlowAddOp,     3, 0, "name ?option value...?",},
+    {"delete",   1, FlowDeleteOp,  2, 0, "name...",},
+    {"names",    1, FlowNamesOp,   2, 3, "?pattern?",},
+    {"next",     2, FlowNextOp,    2, 2, "",},
+    {"reset",    1, FlowResetOp,   2, 2, "",},
+    {"video",    1, FlowVideoOp,   7, 7, 	
+	"width height numFrames frameRate bitRate ",},
+};
+static int nFlowCmdOps = NumCmdSpecs(flowCmdOps);
+
+/*ARGSUSED*/
+static int
+FlowCmdProc(ClientData clientData, Tcl_Interp *interp, int objc,
+	    Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+
+    proc = Rappture::GetOpFromObj(interp, nFlowCmdOps, flowCmdOps, 
+	Rappture::CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    return (*proc) (clientData, interp, objc, objv);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowCmdInitProc --
+ *
+ *	This procedure is invoked to initialize the "tree" command.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Creates the new command and adds a new entry into a global Tcl
+ *	associative array.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+FlowCmdInitProc(Tcl_Interp *interp)
+{
+    Tcl_CreateObjCommand(interp, "flow", FlowCmdProc, NULL, NULL);
+    FlowCmd::Init();
+    return TCL_OK;
+}
+
+
+float
+FlowCmd::GetPosition(FlowPosition *posPtr)
+{
+    if (posPtr->flags == RELPOS) {
+	return posPtr->value;
+    }
+    switch (posPtr->axis) {
+    case AXIS_X:  
+	return (posPtr->value - _xMin) / (_xMax - _xMin); 
+    case AXIS_Y:  
+	return (posPtr->value - _yMin) / (_yMax - _yMin); 
+    case AXIS_Z:  
+	return (posPtr->value - _zMin) / (_zMax - _zMin); 
+    }
+    return 0.0;
+}
+
+float
+FlowCmd::GetPosition(void) 
+{
+    return FlowCmd::GetPosition(&_sv.slicePos);
+}
Index: trunk/packages/vizservers/nanovis/FlowCmd.h
===================================================================
--- trunk/packages/vizservers/nanovis/FlowCmd.h	(revision 1429)
+++ trunk/packages/vizservers/nanovis/FlowCmd.h	(revision 1429)
@@ -0,0 +1,364 @@
+
+struct FlowColor {
+    float r, g, b, a;
+};
+
+struct FlowPosition {
+    float value;
+    unsigned int flags;
+    int axis;
+};
+
+struct FlowPoint {
+    float x, y, z;
+};
+
+struct FlowParticlesValues {
+    int isHidden;			/* Indicates if particle injection
+					 * plance is active or not. */
+    FlowPosition position;		/* Position on axis of particle
+					 * plane */
+    FlowColor color;			/* Color of particles */
+};
+
+struct FlowParticlesIterator {
+    Tcl_HashEntry *hashPtr;
+    Tcl_HashSearch hashSearch;
+};
+
+class FlowParticles {
+    const char *_name;			/* Name of particle injection
+					 * plane. Actual character string is
+					 * stored in hash table. */
+    Tcl_HashEntry *_hashPtr;
+    NvParticleRenderer *_rendererPtr;	/* Particle renderer. */
+    FlowParticlesValues _sv;
+
+    static Rappture::SwitchSpec _switches[];
+public:
+
+    FlowParticles(const char *name, Tcl_HashEntry *hPtr);
+    ~FlowParticles(void) {
+	Rappture::FreeSwitches(_switches, &_sv, 0);
+	if (_rendererPtr != NULL) {
+	    delete _rendererPtr;
+	}
+	if (_hashPtr != NULL) {
+	    Tcl_DeleteHashEntry(_hashPtr);
+	}
+    }
+    void SetColor(FlowColor &color) {
+	_sv.color = color;
+	_rendererPtr->setColor(Vector4(color.r, color.g, color.b, color.a));
+    }
+    const char *name(void) {
+	return _name;
+    }
+    void disconnect(void) {
+	_hashPtr = NULL;
+    }
+    bool visible(void) {
+	return !_sv.isHidden;
+    }
+    int ParseSwitches(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
+	if (Rappture::ParseSwitches(interp, _switches, objc, objv, &_sv, 
+		SWITCH_DEFAULTS) < 0) {
+	    return TCL_ERROR;
+	}
+	return TCL_OK;
+    }
+    void Advect(void) {
+	assert(_rendererPtr->isActivated());
+	_rendererPtr->advect();
+    }
+    void Render(void);
+    void Reset(void) {
+	_rendererPtr->reset();
+    }
+    void Initialize(void) {
+	_rendererPtr->initialize();
+    }
+    void SetVectorField(Volume *volPtr) {
+	_rendererPtr->setVectorField(volPtr->id, 
+            *(volPtr->get_location()),
+            1.0f,
+            volPtr->height / (float)volPtr->width,
+            volPtr->depth  / (float)volPtr->width,
+            volPtr->wAxis.max());
+    }
+    void Configure(void);
+};
+
+struct FlowBoxIterator {
+    Tcl_HashEntry *hashPtr;
+    Tcl_HashSearch hashSearch;
+};
+
+struct FlowBoxValues {
+    int isHidden;			/* Indicates if particle injection
+					 * plance is active or not. */
+    float position;			/* Position on axis of particle
+					 * plane */
+    FlowPoint corner1, corner2;		/* Coordinates of the box. */
+    
+    FlowColor color;			/* Color of particles */
+    float lineWidth;
+};
+
+class FlowBox {
+    const char *_name;			/* Name of this box in the hash
+					 * table. */
+    Tcl_HashEntry *_hashPtr;		/* Pointer to this entry in the hash
+					 * table of boxes. */
+    FlowBoxValues _sv;
+    static Rappture::SwitchSpec _switches[];
+public:
+
+    FlowBox(const char *name, Tcl_HashEntry *hPtr);
+    ~FlowBox(void) {
+	Rappture::FreeSwitches(_switches, &_sv, 0);
+	if (_hashPtr != NULL) {
+	    Tcl_DeleteHashEntry(_hashPtr);
+	}
+    }
+    const char *name(void) {
+	return _name;
+    }
+    bool visible(void) {
+	return !_sv.isHidden;
+    }
+    void disconnect(void) {
+	_hashPtr = NULL;
+    }
+    int ParseSwitches(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
+	if (Rappture::ParseSwitches(interp, _switches, objc, objv, &_sv, 
+		SWITCH_DEFAULTS) < 0) {
+	    return TCL_ERROR;
+	}
+	return TCL_OK;
+    }
+    void Render(Volume *volPtr);
+};
+
+struct FlowValues {
+    TransferFunction *tfPtr;
+    FlowPosition slicePos;
+    int sliceVisible;
+    int showVolume;
+    int showOutline;
+    int isHidden;
+};
+
+struct FlowIterator {
+    Tcl_HashEntry *hashPtr;
+    Tcl_HashSearch hashSearch;
+};
+
+class FlowCmd {
+    Tcl_Interp *_interp;
+    Tcl_HashEntry *_hashPtr;
+    const char *_name;			/* Name of the flow.  This may differ
+					 * from the name of the command
+					 * associated with the flow, if the
+					 * command was renamed. */
+    Tcl_Command _cmdToken;		/* Command associated with the flow.
+					 * When the command is deleted, so is
+					 * the flow. */
+    Rappture::Unirect3d *_dataPtr;	/* Uniform rectangular data
+					 * representing the mesh and vector
+					 * field values.  These values are
+					 * kept to regenerate the volume
+					 * associated with the flow. */
+    Volume *_volPtr;			/* The volume associated with the
+					 * flow.  This isn't the same thing as
+					 * a normal volume displayed. */
+    int _volIndex; 			/* The index of slot in the volume
+					 * vector. -1 indicates that a slot
+					 * hasn't been previously allocated.
+					 * This is to reuse the same slot so
+					 * the volume vector doesn't grow when
+					 * we reallocate vectors. */
+
+    NvVectorField *_fieldPtr;		/* Vector field generated from the 
+					 * above volume. */
+
+    Tcl_HashTable _particlesTable;	/* For each field there can be one or
+					 * more particle injection planes
+					 * where the particles are injected
+					 * into the flow. */
+
+    Tcl_HashTable _boxTable;		/* A table of boxes.  The many be
+					 * zero or more boxes associated
+					 * with each field. */
+
+    void Configure(void);
+
+    /* Overall ranges over all flow vectors. */
+    static float _xMin, _xMax, _yMin, _yMax, _zMin, _zMax, _wMin, _wMax;
+    static float _magMin, _magMax;
+    static float _xOrigin, _yOrigin, _zOrigin;
+
+    static Rappture::SwitchSpec _switches[];
+    FlowValues _sv;
+
+    /* Class-generic data */
+    static Tcl_HashTable _flowTable;
+    static int _initialized;
+
+    void RenderBoxes(void);
+public:
+    static unsigned int flags;
+    enum SliceAxis { AXIS_X, AXIS_Y, AXIS_Z };
+    enum FlowCmdFlags { REDRAW_PENDING=(1<<0), MAP_PENDING=(1<<1) };
+    FlowCmd(Tcl_Interp *interp, const char *name, Tcl_HashEntry *hPtr);
+    ~FlowCmd(void);
+
+    int CreateParticles(Tcl_Interp *interp, Tcl_Obj *objPtr);
+    int GetParticles(Tcl_Interp *interp, Tcl_Obj *objPtr,
+		     FlowParticles **particlePtrPtr);
+    void Render(void);
+    void Advect(void);
+    void ResetParticles(void);
+    void InitializeParticles(void);
+
+    FlowParticles *FirstParticles(FlowParticlesIterator *iterPtr);
+    FlowParticles *NextParticles(FlowParticlesIterator *iterPtr);
+
+    int CreateBox(Tcl_Interp *interp, Tcl_Obj *objPtr);
+    int GetBox(Tcl_Interp *interp, Tcl_Obj *objPtr, FlowBox **boxPtrPtr);
+    FlowBox *FirstBox(FlowBoxIterator *iterPtr);
+    FlowBox *NextBox(FlowBoxIterator *iterPtr);
+
+    static FlowCmd *FirstFlow(FlowIterator *iterPtr);
+    static FlowCmd *NextFlow(FlowIterator *iterPtr);
+    static void Init(void);
+    static int GetFlow(Tcl_Interp *interp, Tcl_Obj *objPtr, 
+		       FlowCmd **flowPtrPtr);
+    static int CreateFlow(Tcl_Interp *interp, Tcl_Obj *objPtr);
+    static void DeleteFlows(Tcl_Interp *interp);
+    static bool MapFlows(void);
+    static void RenderFlows(void);
+    static void ResetFlows(void);
+    static bool UpdateFlows(void);
+    static void AdvectFlows(void);
+
+    float *GetScaledVector(void);
+    Volume *MakeVolume(float *data);
+    
+    void InitVectorField(void);
+
+    NvVectorField *VectorField(void) {
+	return _fieldPtr;
+    }
+
+    bool ScaleVectorField(void);
+
+    bool visible(void) {
+	return !_sv.isHidden;
+    }
+    const char *name(void) {
+	return _name;
+    }
+    void disconnect(void) {
+	_hashPtr = NULL;
+    }
+    bool isDataLoaded(void) {
+	return (_dataPtr != NULL);
+    }
+    Rappture::Unirect3d *GetData(void) {
+	return _dataPtr;
+    }
+    void SetData(Rappture::Unirect3d *dataPtr) {
+	if (_dataPtr != NULL) {
+	    delete _dataPtr;
+	}
+	_dataPtr = dataPtr;
+    }
+    void ActivateSlice(void) {
+	/* Must set axis before offset or position goes to wrong axis. */
+	NanoVis::licRenderer->set_axis(_sv.slicePos.axis);
+	NanoVis::licRenderer->set_offset(_sv.slicePos.value);
+        NanoVis::licRenderer->activate();
+    }
+    void DeactivateSlice(void) {
+        NanoVis::licRenderer->deactivate();
+    }
+    SliceAxis GetAxis(void) {
+	return (SliceAxis)_sv.slicePos.axis;
+    }
+    float GetPosition(void);
+    void SetAxis(void) {
+	NanoVis::licRenderer->set_axis(_sv.slicePos.axis);
+    }
+    void SetAxis(FlowCmd::SliceAxis axis) {
+	_sv.slicePos.axis = axis;
+	NanoVis::licRenderer->set_axis(_sv.slicePos.axis);
+    }
+    void SetCurrentPosition(float position) {
+	_sv.slicePos.value = position;
+	NanoVis::licRenderer->set_offset(_sv.slicePos.value);
+    }
+    void SetCurrentPosition(void) {
+	NanoVis::licRenderer->set_offset(_sv.slicePos.value);
+    }
+    void GetMagRange(double &min_mag, double &max_mag);
+
+    void SetVectorField(NvVectorField *fieldPtr) {
+	DeleteVectorField();
+	_fieldPtr = fieldPtr;
+    }
+    void DeleteVectorField(void) {
+	if (_fieldPtr != NULL) {
+	    delete _fieldPtr;
+	    _fieldPtr = NULL;
+	}
+    }
+    int ParseSwitches(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) {
+	if (Rappture::ParseSwitches(interp, _switches, objc, objv, &_sv,
+		SWITCH_DEFAULTS) < 0) {
+	    return TCL_ERROR;
+	}
+	return TCL_OK;
+    }
+    static void EventuallyRedraw(unsigned int flag = 0) {
+	if (flag) {
+	    flags |= flag;
+	}
+	if ((flags & FlowCmd::REDRAW_PENDING) == 0) {
+	    glutPostRedisplay();
+	    flags |= FlowCmd::REDRAW_PENDING;
+	}
+    }
+    static float GetPosition(FlowPosition *posPtr);
+};
+
+extern int GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes);
+#ifdef notdef
+extern bool SetVectorFieldData(Rappture::Outcome &context, 
+	float xMin, float xMax, size_t xNum, 
+	float yMin, float yMax, size_t yNum, 
+	float zMin, float zMax, size_t zNum, 
+	size_t nValues, float *values, 
+	FlowCmd *flowPtr);
+
+extern bool SetResampledVectorFieldData(Rappture::Outcome &context, 
+	float xMin, float xMax, size_t xNum, 
+	float yMin, float yMax, size_t yNum, 
+	float zMin, float zMax, size_t zNum, 
+	size_t nValues, float *values, 
+	FlowCmd *flowPtr);
+
+extern bool SetVectorFieldDataFromUnirect3d(Rappture::Outcome &context, 
+	Rappture::Unirect3d &data, FlowCmd *flowPtr);
+#endif
+
+extern int GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 
+	bool *boolPtr);
+extern int GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 
+	float *floatPtr);
+extern int GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 
+	int *axisPtr);
+extern int GetVolumeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 
+	Volume **volumePtrPtr);
+
+
Index: trunk/packages/vizservers/nanovis/FlowCmds.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/FlowCmds.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/FlowCmds.cpp	(revision 1429)
@@ -42,6 +42,11 @@
 #include <assert.h>
 #include <stdlib.h>
-#include <tcl.h>
+#include <stddef.h>
+/*#include <tcl.h>*/
+#include "Vector4.h"
 #include "Switch.h"
+#include "Unirect.h"
+#include "FlowCmd.h"
+
 #include <RpField1D.h>
 #include <RpFieldRect3D.h>
@@ -70,24 +75,83 @@
 #include "RenderContext.h"
 #include "NvLIC.h"
-#include "Unirect.h"
-#include "FlowCmd.h"
 
 static Tcl_HashTable flowTable;
 
-static int
-GetFlowFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, FlowCmd **flowPtrPtr)
-{
-    Tcl_HashEntry *hPtr;
-    hPtr = Tcl_FindHashEntry(&flowTable, Tcl_GetString(objPtr));
-    if (hPtr == NULL) {
-	if (interp != NULL) {
-	    Tcl_AppendResult(interp, "can't find a flow \"", 
-			     Tcl_GetString(objPtr), "\"", (char *)NULL);
-	}
-	return TCL_ERROR;
-    }
-    *flowPtrPtr = (FlowCmd *)Tcl_GetHashValue(hPtr);
-    return TCL_OK;
-}
+extern bool load_vector_stream2(Rappture::Outcome &result, int index, 
+	size_t length, Rappture::Unirect3d *dataPtr);
+
+/*
+    physicalMin.set(dataPtr->xMin(), dataPtr->yMin(), dataPtr->zMin());
+    physicalMax.set(dataPtr->xMax(), dataPtr->yMax(), dataPtr->zMax());
+*/
+static float *
+MakeFlowVector(Rappture::Unirect3d *dataPtr, double minMag, double maxMag)
+{
+    float *data = new float[4* dataPtr->nValues()];
+    if (data == NULL) {
+	return NULLL;
+    }
+    memset(data, 0, sizeof(float) * 4 * dataPtr->nValues());
+    float *destPtr = data;
+    float *srcPtr = dataPtr->values();
+    for (size_t iz=0; iz < dataPtr->zNum(); iz++) {
+	for (size_t iy=0; iy < dataPtr->yNum(); iy++) {
+	    for (size_t ix=0; ix < dataPtr->xNum(); ix++) {
+		double vx, vy, vz, vm;
+		vx = values[0];
+		vy = values[1];
+		vz = values[2];
+		vm = sqrt(vx*vx + vy*vy + vz*vz);
+		destPtr[0] = vm / maxMag; 
+		destPtr[1] = vx /(2.0*maxMag) + 0.5; 
+		destPtr[2] = vy /(2.0*maxMag) + 0.5; 
+		destPtr[3] = vz /(2.0*maxMag) + 0.5; 
+		srcPtr += 3;
+		destPtr += 4;
+	    }
+	}
+    }
+    return data;
+}
+
+static Volume *
+MakeFlowVolume(Rappture::Unirect3d *dataPtr, double minMag, double maxMag, 
+	       float *data)
+{
+    Volume *volPtr;
+
+    ivol = NanoVis::n_volumes;
+    volPtr = NanoVis::load_volume(ivol, dataPtr->xNum(), dataPtr->yNum(), 
+	dataPtr->zNum(), 4, data, minMag, maxMag, 0);
+    volPtr->xAxis.SetRange(dataPtr->xMin(), dataPtr->xMax());
+    volPtr->yAxis.SetRange(dataPtr->yMin(), dataPtr->yMax());
+    volPtr->zAxis.SetRange(dataPtr->zMin(), dataPtr->zMax());
+    volPtr->wAxis.SetRange(minMag, maxMag);
+    volPtr->update_pending = true;
+    volPtr->setPhysicalBBox(physicalMin, physicalMax);
+    //
+    // BE CAREFUL:  Set the number of slices to something
+    //   slightly different for each volume.  If we have
+    //   identical volumes at exactly the same position
+    //   with exactly the same number of slices, the second
+    //   volume will overwrite the first, so the first won't
+    //   appear at all.
+    //
+    volPtr->set_n_slice(256-n);
+    // volPtr->set_n_slice(512-n);
+    volPtr->disable_cutplane(0);
+    volPtr->disable_cutplane(1);
+    volPtr->disable_cutplane(2);
+    
+    NanoVis::vol_renderer->add_volume(volPtr, 
+	NanoVis::get_transfunc("default"));
+    
+    float dx0 = -0.5;
+    float dy0 = -0.5*volPtr->height/volPtr->width;
+    float dz0 = -0.5*volPtr->depth/volPtr->width;
+    volPtr->move(Vector3(dx0, dy0, dz0));
+    return volPtr;
+}
+
 
 static int
@@ -95,5 +159,5 @@
 	       Tcl_Obj *const *objv)
 {
-    Rappture::Outcome context;
+    Rappture::Outcome result;
 
     const char *fileName;
@@ -106,7 +170,7 @@
     }
     Rappture::Buffer buf;
-    if (!buf.load(context, fileName)) {
+    if (!buf.load(result, fileName)) {
 	Tcl_AppendResult(interp, "can't load data from \"", fileName, "\": ",
-			 context.remark(), (char *)NULL);
+			 result.remark(), (char *)NULL);
 	return TCL_ERROR;
     }
@@ -114,13 +178,16 @@
     FlowCmd *flowPtr = (FlowCmd *)clientData;
     if (strncmp(buf.bytes(), "<DX>", 4) == 0) {
-	Rappture::Unirect3d data;
-	if (!load_vector_stream2(context, buf.size(), (char *)buf.bytes(), 
-				 data)) {
-	    Tcl_AppendResult(interp, context.remark(), (char *)NULL);
+	Rappture::Unirect3d *dataPtr;
+
+	dataPtr = new Rappture::Unirect3d();
+	if (!dataPtr->ReadVectorDataFromDx(result, buf.size(), 
+		(char *)buf.bytes())) {
+	    Tcl_AppendResult(interp, result.remark(), (char *)NULL);
+	    delete dataPtr;
 	    return TCL_ERROR;
 	}
-	flowPtr->dataPtr = &data;
+	flowPtr->dataPtr = dataPtr;
     } else if (strncmp(buf.bytes(), "<unirect3d>", 4) == 0) {
-	Rappture::Unirect3d data;
+	Rappture::Unirect3d *dataPtr;
 	Tcl_CmdInfo cmdInfo;
 
@@ -130,14 +197,17 @@
 	    return TCL_ERROR;
 	}
-	data.extents(extents);
-	cmdInfo.objClientData = (ClientData)&data;	
+	dataPtr = new Rappture::Unirect3d();
+	dataPtr->extents(extents);
+	cmdInfo.objClientData = (ClientData)dataPtr;	
 	Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
 	if (Tcl_Eval(interp, (const char *)buf.bytes()) != TCL_OK) {
+	    delete dataPtr;
 	    return TCL_ERROR;
 	}
-	if (!data.isInitialized()) {
+	if (!dataPtr->isInitialized()) {
+	    delete dataPtr;
 	    return TCL_ERROR;
 	}
-	flowPtr->dataPtr = &data;
+	flowPtr->dataPtr = dataPtr;
     }
 
@@ -174,5 +244,5 @@
                     Tcl_Obj *const *objv)
 {
-    Rappture::Outcome context;
+    Rappture::Outcome result;
 
     Trace("Flow Data Loading\n");
@@ -192,36 +262,38 @@
     FlowCmd *flowPtr = (FlowCmd *)clientData;
     if (strncmp(buf.bytes(), "<DX>", 4) == 0) {
-	if (!load_vector_stream2(context, buf.size(), (char *)buf.bytes(), 
-		flowPtr)) {
-	    Tcl_AppendResult(interp, context.remark(), (char *)NULL);
+	Rappture::Unirect3d *dataPtr;
+
+	dataPtr = new Rappture::Unirect3d();
+	if (!dataPtr->ReadVectorDataFromDx(result, buf.size(), 
+		(char *)buf.bytes())) {
+	    Tcl_AppendResult(interp, result.remark(), (char *)NULL);
+	    delete dataPtr;
 	    return TCL_ERROR;
 	}
+	flowPtr->dataPtr = dataPtr;
     } else if (strncmp(buf.bytes(), "<unirect3d>", 4) == 0) {
-	Rappture::Unirect3d data;
+	Rappture::Unirect3d *dataPtr;
 	Tcl_CmdInfo cmdInfo;
 
 	/* Set the clientdata field of the unirect3d command to contain
 	 * the local data structure. */
+	dataPtr = new Rappture::Unirect3d();
 	if (!Tcl_GetCommandInfo(interp, "unirect3d", &cmdInfo)) {
 	    return TCL_ERROR;
 	}
-	data.extents(extents);
-	cmdInfo.objClientData = (ClientData)&data;	
+	dataPtr->extents(extents);
+	cmdInfo.objClientData = (ClientData)dataPtr;	
 	Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
 	if (Tcl_Eval(interp, (const char *)buf.bytes()) != TCL_OK) {
+	    delete dataPtr;
 	    return TCL_ERROR;
 	}
-	if (!data.isInitialized()) {
+	if (!dataPtr->isInitialized()) {
+	    delete dataPtr;
 	    return TCL_ERROR;
 	}
-	if (!SetVectorFieldDataFromUnirect3d(context, data, flowPtr)) {
-	    Tcl_AppendResult(interp, context.remark(), (char *)NULL);
-	    return TCL_ERROR;
-	}
-    }
-    if (flowPtr->volPtr != NULL) {
-	delete flowPtr->volPtr;
-    }
-    #ifdef notdef
+	flowPtr->dataPtr = dataPtr;
+    }
+#ifdef notdef
     // FIXME: Move this to flow pending routine.
 
@@ -249,5 +321,5 @@
     float dz0 = -0.5*flowPtr->volPtr->depth/flowPtr->volPtr->width;
     flowPtr->volPtr->move(Vector3(dx0, dy0, dz0));
-    #endif
+#endif
     return TCL_OK;
 }
@@ -273,5 +345,9 @@
 }
 
-
+/*
+ *	Can configure axis of stream plane, the position on the axis, and if
+ *	the stream plane is visible.
+ *
+ */
 static Rappture::SwitchSpec streamSwitches[] = 
 {
@@ -284,5 +360,5 @@
 
 static int
-FlowStreamConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
+FlowSliceConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
 		     Tcl_Obj *const *objv)
 {
@@ -295,8 +371,10 @@
     streamPtr = flowPtr->streams + axis;
 
-    if (Rappture::ParseSwitches(interp, streamSwitches, objc - 3, objv + 3, 
+    if (flowPtr->ParseRappture::ParseSwitches(interp, streamSwitches, objc - 3, objv + 3, 
 	streamPtr, SWITCH_DEFAULTS) < 0) {
 	return TCL_ERROR;
     }
+    // Save the values in the stream
+    streamPtr->axis;
     NanoVis::lic_slice_x = streamPtr->position;
     NanoVis::licRenderer->set_axis(axis);
@@ -324,37 +402,4 @@
     return (*proc) (clientData, interp, objc, objv);
 }
-
-static int
-FlowParticlesVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
-             Tcl_Obj *const *objv)
-{
-    bool state;
-    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
-        return TCL_ERROR;
-    }
-
-    NanoVis::particle_on = state;
-    return TCL_OK;
-}
-
-static Rappture::CmdSpec flowParticlesOps[] = {
-    {"visible",    1, FlowParticlesVisibleOp,  4, 4, "on|off",},
-};
-static int nFlowParticlesOps = NumCmdSpecs(flowParticlesOps);
-
-static int
-FlowParticlesOp(ClientData clientData, Tcl_Interp *interp, int objc,
-             Tcl_Obj *const *objv)
-{
-    Tcl_ObjCmdProc *proc;
-
-    proc = Rappture::GetOpFromObj(interp, nFlowParticlesOps, flowParticlesOps,
-                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
-    if (proc == NULL) {
-        return TCL_ERROR;
-    }
-    return (*proc) (clientData, interp, objc, objv);
-}
-
 
 static int
@@ -387,26 +432,79 @@
 }
 
-static Rappture::SwitchSpec flowSwitches[] = 
-{
-    {Rappture::SWITCH_BOOLEAN, "-lic", "boolean",
-	offsetof(FlowCmd, lic), 0},
+static Rappture::SwitchSpec streamSwitches[] = 
+{
     {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
-	offsetof(FlowCmd, hidden), 0},
+	offsetof(Stream, hidden), 0},
+    {Rappture::SWITCH_FLOAT, "-position", "number",
+	offsetof(Stream, position), 0},
     {Rappture::SWITCH_END}
 };
 
+static Rappture::SwitchParseProc AxisSwitchProc;
+
+static Rappture:;SwitchCustom axisSwitch = {
+    AxisSwitchProc, NULL, 0,
+};
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AxisSwitch --
+ *
+ *	Convert a Tcl_Obj representing the label of a child node into its
+ *	integer node id.
+ *
+ * Results:
+ *	The return value is a standard Tcl result.
+ *
+ *---------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+AxisSwitch(
+    ClientData clientData,	/* Flag indicating if the node is considered
+				 * before or after the insertion position. */
+    Tcl_Interp *interp,		/* Interpreter to send results back to */
+    const char *switchName,	/* Not used. */
+    Tcl_Obj *objPtr,		/* String representation */
+    char *record,		/* Structure record */
+    int offset,			/* Not used. */
+    int flags)			/* Not used. */
+{
+    const char *string = Tcl_GetString(objPtr);
+    if (string[1] == '\0') {
+	FlowCmd::SliceAxis *axisPtr = (FlowCmd::SliceAxis *)(record + offset);
+        char c;
+        c = tolower((unsigned char)string[0]);
+        if (c == 'x') {
+            *axisPtr = FlowCmd::SliceAxis::AXIS_X;
+            return TCL_OK;
+        } else if (c == 'y') {
+            *axisPtr = FlowCmd::SliceAxis::AXIS_Y;
+            return TCL_OK;
+        } else if (c == 'z') {
+            *axisPtr = FlowCmd::SliceAxis::AXIS_Z;
+            return TCL_OK;
+        }
+        /*FALLTHRU*/
+    }
+    Tcl_AppendResult(interp, "bad axis \"", string,
+                     "\": should be x, y, or z", (char*)NULL);
+    return TCL_ERROR;
+}
+
 static int
 FlowConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
 		Tcl_Obj *const *objv)
 {
-    int axis;
-    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
-        return TCL_ERROR;
-    }
-    FlowCmd *flowPtr = (FlowCmd *)clientData;
-    if (Rappture::ParseSwitches(interp, flowSwitches, objc - 3, objv + 3, 
-	flowPtr, SWITCH_DEFAULTS) < 0) {
-	return TCL_ERROR;
-    }
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    if (flowPtr->ParseSwitches(interp, objc - 3, objv + 3) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    flowPtr->SetAxis();
+    flowPtr->SetSlicePosition();
+    flowPtr->SliceVisible();
+    flowPtr->Visible();
     return TCL_OK;
 }
@@ -421,7 +519,132 @@
 };
 
-static int
-FlowInjectorAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
-		Tcl_Obj *const *objv)
+
+static int
+FlowParticlesAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		   Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    if (flowPtr->CreatePlane(interp, objv[3])) {
+	return TCL_ERROR;
+    }
+    Particles *particlesPtr;
+    if (flowPtr->GetPlane(interp, objv[3], &particlesPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
+	flowPtr->DestroyPlane(particlesPtr);
+	return TCL_ERROR;
+    }
+    particlesPtr->SetPosition();
+    particlesPtr->SetColor();
+    particlesPtr->SetAxis();
+    particlesPtr->Visible();
+    return TCL_OK;
+}
+
+static int
+FlowParticlesConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
+			 Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+
+    Particles *particlesPtr;
+    if (flowPtr->GetPlane(interp, objv[3], &particlesPtr) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
+	return TCL_ERROR;
+    }
+    particlesPtr->SetPosition();
+    particlesPtr->SetColor();
+    particlesPtr->SetAxis();
+    particlesPtr->Visible();
+    return TCL_OK;
+}
+
+static int
+FlowParticlesDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		      Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    int i;
+    for (i = 3; i < objc; i++) {
+	Particles *particlesPtr;
+
+	if (flowPtr->GetPlane(interp, objv[i], &particlesPtr)!=TCL_OK) {
+	    return TCL_ERROR;
+	}
+	flowPtr->DestroyPlane(particlesPtr);
+    }
+    return TCL_OK;
+}
+
+static int
+FlowParticlesNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
+		     Tcl_Obj *const *objv)
+{
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    Tcl_Obj *listObjPtr;
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    ParticlesIterator iter;
+    Particles *particlesPtr;
+    for (particlesPtr = flowPtr->FirstPlane(&iter); particlesPtr != NULL; 
+	 particlesPtr = flowPtr->NextPlane(&iter)) {
+
+	objPtr = Tcl_NewStringObj(particlesPtr->Name(), -1);
+	Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+    }
+    Tcl_SetObjResult(interp, listObjPtr);
+    return TCL_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowParticlesObjCmd --
+ *
+ * 	This procedure is invoked to process commands on behalf of the flow
+ * 	object.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	See the user documentation.
+ *
+ * $flow particles oper $name 
+ *---------------------------------------------------------------------------
+ */
+static Rappture::CmdSpec flowParticlesOps[] = {
+    {"add",        1, FlowParticlesAddOp,        3, 0, "name ?switches?",},
+    {"configure",  1, FlowParticlesConfigureOp,  3, 0, "name ?switches?",},
+    {"delete",	   1, FlowParticlesDeleteOp,     3, 0, "?name...?"},
+    {"names",      1, FlowParticlesNamesOp,      3, 4, "?pattern?"},
+};
+
+static int nFlowInjectorOps = NumCmdSpecs(flowInjectorOps);
+
+static int
+FlowInjectorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+	       Tcl_Obj *const *objv)
+{
+    Tcl_ObjCmdProc *proc;
+    proc = Rappture::GetOpFromObj(interp, nFlowInjectorOps, flowInjectorOps, 
+	Rappture::CMDSPEC_ARG1, objc, objv, 0);
+    if (proc == NULL) {
+	return TCL_ERROR;
+    }
+    FlowCmd *flowPtr = (FlowCmd *)clientData;
+    Tcl_Preserve(flowPtr);
+    int result;
+    result = (*proc) (clientData, interp, objc, objv);
+    Tcl_Release(flowPtr);
+    return result;
+}
+
+static int
+FlowShapeAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
+	       Tcl_Obj *const *objv)
 {
     FlowCmd *flowPtr = (FlowCmd *)clientData;
@@ -456,5 +679,5 @@
 
 static int
-FlowInjectorDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
+FlowShapeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
 		   Tcl_Obj *const *objv)
 {
@@ -476,5 +699,5 @@
 
 static int
-FlowInjectorNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
+FlowShapeNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
              Tcl_Obj *const *objv)
 {
@@ -483,4 +706,5 @@
     Tcl_HashSearch iter;
     Tcl_Obj *listObjPtr;
+
     listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
     for (hPtr = Tcl_FirstHashEntry(&flowPtr->injectTable, &iter);
@@ -498,5 +722,5 @@
 
 static int
-FlowInjectorConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
+FlowShapeConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
 			Tcl_Obj *const *objv)
 {
@@ -519,9 +743,8 @@
 }
 
-
-/*
- *---------------------------------------------------------------------------
- *
- * FlowInjectorObjCmd --
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FlowShapeOp--
  *
  * 	This procedure is invoked to process commands on behalf of the flow
@@ -536,19 +759,19 @@
  *---------------------------------------------------------------------------
  */
-static Rappture::CmdSpec flowInjectorOps[] = {
-    {"add",        1, FlowInjectorAddOp,        3, 0, "name ?switches?",},
-    {"configure",  1, FlowInjectorConfigureOp,  3, 5, "name ?switches?",},
-    {"delete",	   1, FlowInjectorDeleteOp,     3, 0, "?name...?"},
-    {"names",      1, FlowInjectorNamesOp,      3, 4, "?pattern?"},
+static Rappture::CmdSpec flowShapeOps[] = {
+    {"add",        1, FlowShapeAddOp,        3, 0, "name ?switches?",},
+    {"configure",  1, FlowShapeConfigureOp,  3, 5, "name ?switches?",},
+    {"delete",	   1, FlowShapeDeleteOp,     3, 0, "?name...?"},
+    {"names",      1, FlowShapeNamesOp,      3, 4, "?pattern?"},
 };
 
-static int nFlowInjectorOps = NumCmdSpecs(flowInjectorOps);
-
-static int
-FlowInjectorOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+static int nFlowShapeOps = NumCmdSpecs(flowShapeOps);
+
+static int
+FlowShapeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
 	       Tcl_Obj *const *objv)
 {
     Tcl_ObjCmdProc *proc;
-    proc = Rappture::GetOpFromObj(interp, nFlowInjectorOps, flowInjectorOps, 
+    proc = Rappture::GetOpFromObj(interp, nFlowShapeOps, flowShapeOps, 
 	Rappture::CMDSPEC_ARG1, objc, objv, 0);
     if (proc == NULL) {
@@ -566,5 +789,5 @@
  *---------------------------------------------------------------------------
  *
- * FlowShapeOp--
+ * FlowInstObjCmd --
  *
  * 	This procedure is invoked to process commands on behalf of the flow
@@ -579,19 +802,18 @@
  *---------------------------------------------------------------------------
  */
-static Rappture::CmdSpec flowShapeOps[] = {
-    {"add",        1, FlowInjectorAddOp,        3, 0, "name ?switches?",},
-    {"configure",  1, FlowInjectorConfigureOp,  3, 5, "name ?switches?",},
-    {"delete",	   1, FlowInjectorDeleteOp,     3, 0, "?name...?"},
-    {"names",      1, FlowInjectorNamesOp,      3, 4, "?pattern?"},
+static Rappture::CmdSpec flowInstOps[] = {
+    {"configure",   1, FlowConfigureOp,  3, 5, "?switches?",},
+    {"data",	    1, FlowDataOp,       4, 0, "oper ?args?"},
+    {"particles",   1, FlowParticlesOp,  3, 0, "oper ?args?",},
+    {"shape",       2, FlowShapeOp,      3, 0, "oper ?args?",},
 };
-
-static int nFlowShapeOps = NumCmdSpecs(flowShapeOps);
-
-static int
-FlowShapeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
+static int nFlowInstOps = NumCmdSpecs(flowInstOps);
+
+static int
+FlowInstObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
 	       Tcl_Obj *const *objv)
 {
     Tcl_ObjCmdProc *proc;
-    proc = Rappture::GetOpFromObj(interp, nFlowShapeOps, flowShapeOps, 
+    proc = Rappture::GetOpFromObj(interp, nFlowInstOps, flowInstOps, 
 	Rappture::CMDSPEC_ARG1, objc, objv, 0);
     if (proc == NULL) {
@@ -609,48 +831,4 @@
  *---------------------------------------------------------------------------
  *
- * FlowInstObjCmd --
- *
- * 	This procedure is invoked to process commands on behalf of the flow
- * 	object.
- *
- * Results:
- *	A standard Tcl result.
- *
- * Side effects:
- *	See the user documentation.
- *
- *---------------------------------------------------------------------------
- */
-static Rappture::CmdSpec flowInstOps[] = {
-    {"configure",   1, FlowConfigureOp,  3, 5, "?switches?",},
-    {"data",	    1, FlowDataOp,       4, 0, "oper ?args?"},
-    {"injector",    1, FlowInjectorOp,   3, 0, "oper ?args?",},
-    {"particles",   1, FlowParticlesOp,  3, 0, "oper ?args?",},
-    {"shape",       2, FlowShapeOp,      3, 0, "oper ?args?",},
-    {"stream",      2, FlowStreamOp,     4, 0, "oper axis ?args?",},
-};
-static int nFlowInstOps = NumCmdSpecs(flowInstOps);
-
-static int
-FlowInstObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
-	       Tcl_Obj *const *objv)
-{
-    Tcl_ObjCmdProc *proc;
-    proc = Rappture::GetOpFromObj(interp, nFlowInstOps, flowInstOps, 
-	Rappture::CMDSPEC_ARG1, objc, objv, 0);
-    if (proc == NULL) {
-	return TCL_ERROR;
-    }
-    FlowCmd *flowPtr = (FlowCmd *)clientData;
-    Tcl_Preserve(flowPtr);
-    int result;
-    result = (*proc) (clientData, interp, objc, objv);
-    Tcl_Release(flowPtr);
-    return result;
-}
-
-/*
- *---------------------------------------------------------------------------
- *
  * FlowInstDeleteProc --
  *
@@ -667,8 +845,4 @@
 {
     FlowCmd *flowPtr = (FlowCmd *)clientData;
-
-    if (flowPtr->hashPtr != NULL) {
-	Tcl_DeleteHashEntry(flowPtr->hashPtr);
-    }
     delete flowPtr;
 }
@@ -686,35 +860,5 @@
 	  Tcl_Obj *const *objv)
 {
-    const char *name;
-    name = Tcl_GetString(objv[2]);
-
-    Tcl_HashEntry *hPtr;
-    int isNew;
-    hPtr = Tcl_CreateHashEntry(&flowTable, name, &isNew);
-    if (!isNew) {
-	Tcl_AppendResult(interp, "flow \"", name, "\" already exists.",
-			 (char *)NULL);
-	return TCL_ERROR;
-    }
-    Tcl_CmdInfo cmdInfo;
-    if (Tcl_GetCommandInfo(interp, name, &cmdInfo)) {
-	Tcl_AppendResult(interp, "an another command \"", name, 
-			 "\" already exists.", (char *)NULL);
-	return TCL_ERROR;
-    }	
-    FlowCmd *flowPtr;
-    flowPtr = new FlowCmd;
-    if (flowPtr == NULL) {
-	Tcl_AppendResult(interp, "can't allocate a flow object \"", name, 
-			 "\"", (char *)NULL);
-	return TCL_ERROR;
-    }
-    flowPtr->hashPtr = hPtr;
-    flowPtr->name = Tcl_GetHashKey(&flowTable, hPtr);
-    flowPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)name, 
-	(Tcl_ObjCmdProc *)FlowInstObjCmd, flowPtr, FlowInstDeleteProc);
-    Tcl_SetHashValue(hPtr, flowPtr);
-    flowPtr->volPtr = NULL;
-    return TCL_OK;
+    return FlowCmd::NewFlow(interp, Tcl_GetString(objv[2]));
 }
 
@@ -736,11 +880,7 @@
 	FlowCmd *flowPtr;
 
-	if (GetFlowFromObj(interp, objv[i], &flowPtr) != TCL_OK) {
+	if (FlowCmd::GetFlowFromObj(interp, objv[i], &flowPtr) != TCL_OK) {
 	    return TCL_ERROR;
 	}
-	Tcl_DeleteCommandFromToken(interp, flowPtr->cmdToken);
-#ifdef notdef
-	NanoVis::flowVisRenderer->removeVectorField(flowPtr);
-#endif
 	delete flowPtr;
     }
@@ -890,8 +1030,6 @@
 
     // Generate the latest frame and send it back to the client
-    if (NanoVis::licRenderer->isActivated()) {
-        NanoVis::licRenderer->convolve();
-    }
-    NanoVis::flowVisRenderer->advect();
+    NanoVis::licRenderer->convolve();
+    FlowCmd::AdvectFlows();
     NanoVis::offscreen_buffer_capture();  //enable offscreen render
     NanoVis::display();
@@ -943,11 +1081,14 @@
 	return TCL_ERROR;
     }
-
-    if (NanoVis::licRenderer) {
-        NanoVis::licRenderer->activate();
-    }
-    if (NanoVis::flowVisRenderer) {
-        NanoVis::flowVisRenderer->activate();
-    }
+    if (NanoVis::licRenderer == NULL) {
+	Tcl_AppendResult(interp, "no lic renderer.", (char *)NULL);
+	return TCL_ERROR;
+    }
+    if (NanoVis::flowVisRenderer == NULL) {
+	Tcl_AppendResult(interp, "no flow renderer.", (char *)NULL);
+	return TCL_ERROR;
+    }
+    NanoVis::licRenderer->activate();
+    NanoVis::flowVisRenderer->activate();
 
     // Save the old dimensions of the offscreen buffer.
@@ -966,5 +1107,5 @@
     Trace("FLOW started\n");
 
-    Rappture::Outcome context;
+    Rappture::Outcome result;
     Rappture::AVTranslate movie(width, height, frameRate, bitRate);
 
@@ -974,16 +1115,10 @@
     }
 
-    movie.init(context, fileName);
+    movie.init(result, fileName);
 
     for (int i = 0; i < numFrames; i++) {
         // Generate the latest frame and send it back to the client
-        if (NanoVis::licRenderer &&
-            NanoVis::licRenderer->isActivated()) {
-            NanoVis::licRenderer->convolve();
-        }
-        if (NanoVis::flowVisRenderer &&
-            NanoVis::flowVisRenderer->isActivated()) {
-            NanoVis::flowVisRenderer->advect();
-        }
+	NanoVis::licRenderer->convolve();
+	FlowCmd::AdvectFlows();
         NanoVis::offscreen_buffer_capture();  //enable offscreen render
         NanoVis::display();
@@ -994,9 +1129,9 @@
         // This is done before bmp_write_to_file because bmp_write_to_file
         // turns rgb data to bgr
-        movie.append(context, NanoVis::screen_buffer, pad);
+        movie.append(result, NanoVis::screen_buffer, pad);
         // NanoVis::bmp_write_to_file(frame_count, fileName);
     }
 
-    movie.done(context);
+    movie.done(result);
     Trace("FLOW end\n");
 
@@ -1011,7 +1146,7 @@
     // FIXME: find a way to get the data from the movie object as a void*
     Rappture::Buffer data;
-    if (!data.load(context, fileName)) {
+    if (!data.load(result, fileName)) {
         Tcl_AppendResult(interp, "can't load data from temporary movie file \"",
-		fileName, "\": ", context.remark(), (char *)NULL);
+		fileName, "\": ", result.remark(), (char *)NULL);
 	return TCL_ERROR;
     }
@@ -1088,111 +1223,2 @@
 }
 
-#ifdef notdef
-void
-MapFlowVectors(Tcl_Interp *interp)
-{
-    double xMin, xMax, yMin, yMax, zMin, zMax, wMin, wMax;
-
-    if (debug_flag) {
-        fprintf(stderr, "in SetHeightmapRanges\n");
-    }
-    xMin = yMin = zMin = wMin = DBL_MAX;
-    xMax = yMax = zMax = wMax = -DBL_MAX;
-    Tcl_HashEntry *hPtr;
-    Tcl_HashSearch iter;
-    for (hPtr = Tcl_FirstHashEntry(&flowTable, &iter); hPtr != NULL; 
-	hPtr = Tcl_NextHashEntry(&iter)) {
-	FlowCmd *flowPtr;
-	flowPtr = (FlowCmd *)Tcl_GetHashValue(hPtr);
-
-	flowPtr->
-
-    for (unsigned int i = 0; i < heightMap.size(); i++) {
-        HeightMap *hmPtr;
-
-        hmPtr = heightMap[i];
-        if (hmPtr == NULL) {
-            continue;
-        }
-        if (xMin > hmPtr->xAxis.min()) {
-            xMin = hmPtr->xAxis.min();
-        }
-        if (xMax < hmPtr->xAxis.max()) {
-            xMax = hmPtr->xAxis.max();
-        }
-        if (yMin > hmPtr->yAxis.min()) {
-            yMin = hmPtr->yAxis.min();
-        }
-        if (yMax < hmPtr->yAxis.max()) {
-            yMax = hmPtr->yAxis.max();
-        }
-        if (zMin > hmPtr->zAxis.min()) {
-            zMin = hmPtr->zAxis.min();
-        }
-        if (zMax < hmPtr->zAxis.max()) {
-            zMax = hmPtr->zAxis.max();
-        }
-        if (wMin > hmPtr->wAxis.min()) {
-            wMin = hmPtr->wAxis.min();
-        }
-        if (wMax < hmPtr->wAxis.max()) {
-            wMax = hmPtr->wAxis.max();
-        }
-    }
-    if ((xMin < DBL_MAX) && (xMax > -DBL_MAX)) {
-        grid->xAxis.SetScale(xMin, xMax);
-    }
-    if ((yMin < DBL_MAX) && (yMax > -DBL_MAX)) {
-        grid->yAxis.SetScale(yMin, yMax);
-    }
-    if ((zMin < DBL_MAX) && (zMax > -DBL_MAX)) {
-        grid->zAxis.SetScale(zMin, zMax);
-    }
-    if ((wMin < DBL_MAX) && (wMax > -DBL_MAX)) {
-        HeightMap::valueMin = grid->yAxis.min();
-        HeightMap::valueMax = grid->yAxis.max();
-    }
-    for (unsigned int i = 0; i < heightMap.size(); i++) {
-        HeightMap *hmPtr;
-
-        hmPtr = heightMap[i];
-        if (hmPtr == NULL) {
-            continue;
-        }
-        hmPtr->MapToGrid(grid);
-    }
-    HeightMap::update_pending = false;
-    if (debug_flag) {
-        fprintf(stderr, "leaving SetHeightmapRanges\n");
-    }
-}
-
-    Tcl_Obj *listObjPtr;
-    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
-
-    Tcl_HashEntry *hPtr;
-    Tcl_HashSearch iter;
-    for (hPtr = Tcl_FirstHashEntry(&flowTable, &iter); hPtr != NULL; 
-	hPtr = Tcl_NextHashEntry(&iter)) {
-	FlowCmd *flowPtr;
-	flowPtr = (FlowCmd *)Tcl_GetHashValue(hPtr);
-	if (flowPtr->fieldPtr != NULL) {
-	    delete flowPtr->fieldPtr; // Remove the vector field.
-	    flowPtr->fieldPtr = NULL;
-	}
-    }
-
-
-            Volume *volPtr;
-            volPtr = NanoVis::load_volume(index, nx, ny, nz, 4, data,
-                        field.valueMin(), field.valueMax(), nzero_min);
-            volPtr->xAxis.SetRange(field.rangeMin(Rappture::xaxis),
-                   field.rangeMax(Rappture::xaxis));
-            volPtr->yAxis.SetRange(field.rangeMin(Rappture::yaxis),
-                   field.rangeMax(Rappture::yaxis));
-            volPtr->zAxis.SetRange(field.rangeMin(Rappture::zaxis),
-                   field.rangeMax(Rappture::zaxis));
-            volPtr->wAxis.SetRange(field.valueMin(), field.valueMax());
-            volPtr->update_pending = true;
-            delete [] data;
-#endif
Index: trunk/packages/vizservers/nanovis/LoadVector.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/LoadVector.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/LoadVector.cpp	(revision 1429)
@@ -12,37 +12,11 @@
 #include "FlowCmd.h"
 
-static INLINE char *
-skipspaces(char *string) 
+    
+
+bool
+ScaleVectorFieldData(Rappture::Outcome &context, Rappture::Unirect3d *dataPtr)
 {
-    while (isspace(*string)) {
-	string++;
-    }
-    return string;
-}
-
-static INLINE char *
-getline(char **stringPtr, char *endPtr) 
-{
-    char *line, *p;
-
-    line = skipspaces(*stringPtr);
-    for (p = line; p < endPtr; p++) {
-	if (*p == '\n') {
-	    *p++ = '\0';
-	    *stringPtr = p;
-	    return line;
-	}
-    }
-    *stringPtr = p;
-    return line;
-}
-
-bool
-SetVectorFieldData(Rappture::Outcome &context, 
-		   float xMin, float xMax, size_t xNum, 
-		   float yMin, float yMax, size_t yNum, 
-		   float zMin, float zMax, size_t zNum, 
-		   size_t nValues, float *values, Unirect3d *dataPtr)
-{
+    float *values = dataPtr->values();
+    float *nValues = dataPtr->nValues();
 #ifdef notdef
     double max_x = -1e21, min_x = 1e21;
@@ -74,5 +48,4 @@
 	    xNum, yNum, zNum, xNum * yNum * zNum);
     
-    // Generate the uniformly sampled rectangle that we need for a volume
     float *destPtr = data;
     float *srcPtr = values;
@@ -97,9 +70,5 @@
 	}
     }
-    dataPtr->xMin(xMin);
-    dataPtr->xMax(xMax);
-    dataPtr->xNum(xNum);
-    dataPtr->yMin(yMin);
-    dataPtr->yMax(yMax);
+    dataPtr->SetValues(xMin, xMax, xNum, yMin, yMax(yMax);
     dataPtr->yNum(yNum);
     dataPtr->yMin(zMin);
@@ -113,13 +82,10 @@
 
 bool
-SetResampledVectorFieldData(Rappture::Outcome &context, 
-			    float xMin, float xMax, size_t xNum, 
-			    float yMin, float yMax, size_t yNum, 
-			    float zMin, float zMax, size_t zNum, 
-			    size_t nValues, float *values, FlowCmd *flowPtr)
+ScaleResampledVectorFieldData(Rappture::Outcome &context, 
+			      Rappture::Unirect3d *dataPtr)
 {
-    Rappture::Mesh1D xgrid(xMin, xMax, xNum);
-    Rappture::Mesh1D ygrid(yMin, yMax, yNum);
-    Rappture::Mesh1D zgrid(zMin, zMax, zNum);
+    Rappture::Mesh1D xgrid(dataPtr->xMin(), dataPtr->xMax(), dataPtr->xNum());
+    Rappture::Mesh1D ygrid(dataPtr->yMin(), dataPtr->yMax(), dataPtr->yNum());
+    Rappture::Mesh1D zgrid(dataPtr->zMin(), dataPtr->zMax(), dataPtr->zNum());
     Rappture::FieldRect3D xfield(xgrid, ygrid, zgrid);
     Rappture::FieldRect3D yfield(xgrid, ygrid, zgrid);
@@ -135,10 +101,10 @@
     max_mag = -1e21, nzero_min = min_mag = 1e21;
     size_t i, j;
-    for (i = j = 0; i < nValues; i += 3, j++) {
+    for (i = j = 0; i < dataPtr->nValues(); i += 3, j++) {
 	double vx, vy, vz, vm;
 
-	vx = values[i];
-	vy = values[i+1];
-	vz = values[i+2];
+	vx = dataPtr->values[i];
+	vy = dataPtr->values[i+1];
+	vz = dataPtr->values[i+2];
 	
 	xfield.define(j, vx);
@@ -217,5 +183,5 @@
 	}
     }
-    
+    dataPtr->zMin()
     flowPtr->xMin = xMin;
     flowPtr->xMax = xMax;
@@ -246,5 +212,4 @@
 
 
-#ifdef notdef
 /*
  * Load a 3D vector field from a dx-format file
@@ -371,3 +336,2 @@
 }
 
-#endif
Index: trunk/packages/vizservers/nanovis/Makefile.in
===================================================================
--- trunk/packages/vizservers/nanovis/Makefile.in	(revision 1428)
+++ trunk/packages/vizservers/nanovis/Makefile.in	(revision 1429)
@@ -104,4 +104,8 @@
 		Event.o \
 		Unirect.o \
+		FlowCmd.o \
+		FlowCmds.o \
+		LoadVector.o \
+		Switch.o \
 		GradientFilter.o \
 		Grid.o \
Index: trunk/packages/vizservers/nanovis/NvFlowVisRenderer.h
===================================================================
--- trunk/packages/vizservers/nanovis/NvFlowVisRenderer.h	(revision 1428)
+++ trunk/packages/vizservers/nanovis/NvFlowVisRenderer.h	(revision 1429)
@@ -44,4 +44,7 @@
 	void reset();
 	void render();
+
+	void addVectorField(Volume* volPtr, const Vector3& ori, float scaleX, 
+		float scaleY, float scaleZ, float max);
 
 	void addVectorField(const std::string& vfName, Volume* volPtr, const Vector3& ori, float scaleX, float scaleY, float scaleZ, float max);
Index: trunk/packages/vizservers/nanovis/NvLIC.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/NvLIC.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/NvLIC.cpp	(revision 1429)
@@ -308,4 +308,5 @@
     float x1, x2, y, px, py;
     
+    glPushMatrix();
     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
     
@@ -313,4 +314,6 @@
     glLoadIdentity(); 
     glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT);
+
+    /*glPushMatrix();*/
     glViewport(0, 0, (GLsizei) NPIX, (GLsizei) NPIX);
     //glTranslatef(-1.0, -1.0, 0.0); 
@@ -397,5 +400,5 @@
     glPopMatrix();
     glMatrixMode(GL_PROJECTION);
-    glPopMatrix();
+    /*glPopMatrix();*/
     glMatrixMode(GL_MODELVIEW);
 }
@@ -488,6 +491,6 @@
 
 void 
-NvLIC::setVectorField(unsigned int texID, const Vector3& ori, float scaleX, float scaleY, 
-                      float scaleZ, float max)
+NvLIC::setVectorField(unsigned int texID, const Vector3& ori, 
+		      float scaleX, float scaleY, float scaleZ, float max)
 {
     Trace("NvLIC: vector field is assigned [%d]\n", texID);
@@ -531,12 +534,10 @@
 NvLIC::set_offset(float v)
 {
-	switch (axis)
-	{
-	case 0 : offset.x = v; break;
-	case 1 : offset.y = v; break;
-	case 2 : offset.z = v; break;
-	}
-
-	get_slice();
+    switch (axis) {
+    case 0 : offset.x = v; break;
+    case 1 : offset.y = v; break;
+    case 2 : offset.z = v; break;
+    }
+    get_slice();
 }
 
Index: trunk/packages/vizservers/nanovis/NvParticleRenderer.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/NvParticleRenderer.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/NvParticleRenderer.cpp	(revision 1429)
@@ -25,5 +25,4 @@
 #include <stdlib.h>
 
-
 #define NV_32
 
@@ -51,5 +50,5 @@
     m_vertex_array = new RenderVertexArray(psys_width*psys_height, 3, GL_FLOAT);
 
-    assert(glGetError()==0);
+    assert(CheckGL(AT));
 
     glGenFramebuffersEXT(2, psys_fbo);
@@ -131,4 +130,6 @@
     glDeleteFramebuffersEXT(2, psys_fbo);
 
+    delete m_vertex_array;
+    delete _advectionShader;
     free(data);
 }
@@ -244,5 +245,5 @@
     if (reborn) 
         reset();
-
+    
     glDisable(GL_BLEND);
     glDisable(GL_DEPTH_TEST);
Index: trunk/packages/vizservers/nanovis/Switch.h
===================================================================
--- trunk/packages/vizservers/nanovis/Switch.h	(revision 1428)
+++ trunk/packages/vizservers/nanovis/Switch.h	(revision 1429)
@@ -34,10 +34,4 @@
 #  include <stddef.h>
 #endif /* HAVE_STDDEF_H */
-
-#ifdef offsetof
-#define SwitchOffset(type, field) ((int) offsetof(type, field))
-#else
-#define SwitchOffset(type, field) ((int) ((char *) &((type *) 0)->field))
-#endif
 
 namespace Rappture {
Index: trunk/packages/vizservers/nanovis/Trace.h
===================================================================
--- trunk/packages/vizservers/nanovis/Trace.h	(revision 1428)
+++ trunk/packages/vizservers/nanovis/Trace.h	(revision 1429)
@@ -5,4 +5,8 @@
 #include <GL/glut.h>
 
+#define MAKE_STRING(x) #x
+#define NEWSTRING(x) MAKE_STRING(x)
+#define AT __FILE__ ":" NEWSTRING(__LINE__)
+
 extern void Trace(const char* format, ...);
 extern bool CheckFBO(GLenum *statusPtr);
Index: trunk/packages/vizservers/nanovis/Unirect.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/Unirect.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/Unirect.cpp	(revision 1429)
@@ -1,9 +1,38 @@
 
+#include <float.h>
 #include <tcl.h>
 #include <Unirect.h>
+#include "RpField1D.h"
+#include "RpFieldRect3D.h"
 
 extern int GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, 
 	float *valuePtr);
 extern int GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr);
+
+static INLINE char *
+skipspaces(char *string) 
+{
+    while (isspace(*string)) {
+	string++;
+    }
+    return string;
+}
+
+static INLINE char *
+getline(char **stringPtr, char *endPtr) 
+{
+    char *line, *p;
+
+    line = skipspaces(*stringPtr);
+    for (p = line; p < endPtr; p++) {
+	if (*p == '\n') {
+	    *p++ = '\0';
+	    *stringPtr = p;
+	    return line;
+	}
+    }
+    *stringPtr = p;
+    return line;
+}
 
 int 
@@ -30,5 +59,4 @@
     axis3 = 0;			/* X-axis */
 
-    int extents = 1;
     values = NULL;
     num[0] = num[1] = num[2] = nValues = 0;
@@ -115,5 +143,5 @@
 	    }
         } else if ((c == 'u') && (strcmp(string, "units") == 0)) {
-            vUnits_ = strdup(Tcl_GetString(objv[i+1]));
+            _vUnits = strdup(Tcl_GetString(objv[i+1]));
         } else if ((c == 'o') && (strcmp(string, "order") == 0)) {
 	    Tcl_Obj **axes;
@@ -142,5 +170,5 @@
         return TCL_ERROR;
     }
-    if (nValues != (num[0] * num[1] * num[2] * extents)) {
+    if ((size_t)nValues != (num[0] * num[1] * num[2] * _nComponents)) {
         Tcl_AppendResult(interp, 
 		"wrong number of values: must be xnum*ynum*znum*extents", 
@@ -149,7 +177,4 @@
     }
     
-    fprintf(stderr, "generating %dx%dx%dx%d = %d points\n", 
-	    num[0], num[1], num[2], extents, num[0]*num[1]*num[2]*extents);
-
     if ((axis1 != 2) || (axis2 != 1) || (axis3 != 0)) {
 	// Reorder the data into x, y, z where x varies fastest and so on.
@@ -165,12 +190,12 @@
 
 		for (x = 0; x < num[2]; x++) {
-		    int i, v;
+		    int i;
 		    
 		    /* Compute the index from the data's described ordering. */
 		    i = ((z*num[axis2]*num[axis3]) + (y*num[axis3]) + x) * 3;
-		    for(v = 0; v < extents; v++) {
+		    for(size_t v = 0; v < _nComponents; v++) {
 			dp[v] = values[i+v];
 		    }
-		    dp += extents;
+		    dp += _nComponents;
 		}
 	    }
@@ -180,29 +205,28 @@
     }
 
-    values_ = values;
-    nValues_ = nValues;
-    extents_ = extents;
+    _values = values;
+    _nValues = nValues;
     if (units[3] != NULL) {
-	vUnits_ = strdup(units[3]);
-    }
-    xMin_ = min[axis3];
-    xMax_ = max[axis3];
-    xNum_ = num[axis3];
+	_vUnits = strdup(units[3]);
+    }
+    _xMin = min[axis3];
+    _xMax = max[axis3];
+    _xNum = num[axis3];
     if (units[axis3] != NULL) {
-	xUnits_ = strdup(units[axis3]);
-    }
-    yMin_ = min[axis2];
-    yMax_ = max[axis2];
-    yNum_ = num[axis2];
+	_xUnits = strdup(units[axis3]);
+    }
+    _yMin = min[axis2];
+    _yMax = max[axis2];
+    _yNum = num[axis2];
     if (units[axis2] != NULL) {
-	yUnits_ = strdup(units[axis2]);
-    }
-    zMin_ = min[axis1];
-    zMax_ = max[axis1];
-    zNum_ = num[axis1];
+	_yUnits = strdup(units[axis2]);
+    }
+    _zMin = min[axis1];
+    _zMax = max[axis1];
+    _zNum = num[axis1];
     if (units[axis1] != NULL) {
-	zUnits_ = strdup(units[axis1]);
-    }
-    initialized_ = true;
+	_zUnits = strdup(units[axis1]);
+    }
+    _initialized = true;
     return TCL_OK;
 }
@@ -225,21 +249,20 @@
     axis[1] = 0;			/* Y-axis */
 
-    extents_ = 1;
-    xNum_ = yNum_ = nValues_ = 0;
-    xMin_ = yMin_ = xMax_ = yMax_ = 0.0f;
-    if (xUnits_ != NULL) {
-	free(xUnits_);
-    }
-    if (yUnits_ != NULL) {
-	free(yUnits_);
-    }
-    if (vUnits_ != NULL) {
-	free(vUnits_);
-    }
-    xUnits_ = yUnits_ = vUnits_ = NULL;
-    if (values_ != NULL) {
-	delete [] values_;
-    }
-    values_ = NULL;
+    _xNum = _yNum = _nValues = 0;
+    _xMin = _yMin = _xMax = _yMax = 0.0f;
+    if (_xUnits != NULL) {
+	free(_xUnits);
+    }
+    if (_yUnits != NULL) {
+	free(_yUnits);
+    }
+    if (_vUnits != NULL) {
+	free(_vUnits);
+    }
+    _xUnits = _yUnits = _vUnits = NULL;
+    if (_values != NULL) {
+	delete [] _values;
+    }
+    _values = NULL;
 
     int i;
@@ -251,9 +274,9 @@
         c = string[0];
         if ((c == 'x') && (strcmp(string, "xmin") == 0)) {
-            if (GetFloatFromObj(interp, objv[i+1], &xMin_) != TCL_OK) {
+            if (GetFloatFromObj(interp, objv[i+1], &_xMin) != TCL_OK) {
                 return TCL_ERROR;
             }
         } else if ((c == 'x') && (strcmp(string, "xmax") == 0)) {
-            if (GetFloatFromObj(interp, objv[i+1], &xMax_) != TCL_OK) {
+            if (GetFloatFromObj(interp, objv[i+1], &_xMax) != TCL_OK) {
                 return TCL_ERROR;
             }
@@ -268,13 +291,13 @@
                 return TCL_ERROR;
             }
-	    xNum_ = n;
+	    _xNum = n;
         } else if ((c == 'x') && (strcmp(string, "xunits") == 0)) {
-            xUnits_ = strdup(Tcl_GetString(objv[i+1]));
+            _xUnits = strdup(Tcl_GetString(objv[i+1]));
         } else if ((c == 'y') && (strcmp(string, "ymin") == 0)) {
-            if (GetFloatFromObj(interp, objv[i+1], &yMin_) != TCL_OK) {
+            if (GetFloatFromObj(interp, objv[i+1], &_yMin) != TCL_OK) {
                 return TCL_ERROR;
             }
         } else if ((c == 'y') && (strcmp(string, "ymax") == 0)) {
-            if (GetFloatFromObj(interp, objv[i+1], &yMax_) != TCL_OK) {
+            if (GetFloatFromObj(interp, objv[i+1], &_yMax) != TCL_OK) {
                 return TCL_ERROR;
             }
@@ -289,7 +312,7 @@
                 return TCL_ERROR;
             }
-	    yNum_ = n;
+	    _yNum = n;
         } else if ((c == 'y') && (strcmp(string, "yunits") == 0)) {
-            yUnits_ = strdup(Tcl_GetString(objv[i+1]));
+            _yUnits = strdup(Tcl_GetString(objv[i+1]));
         } else if ((c == 'v') && (strcmp(string, "values") == 0)) {
             Tcl_Obj **vobj;
@@ -304,14 +327,14 @@
                 return TCL_ERROR;
 	    }
-	    nValues_ = n;
-            values_ = new float[nValues_];
+	    _nValues = n;
+            _values = new float[_nValues];
             size_t j;
-            for (j = 0; j < nValues_; j++) {
-                if (GetFloatFromObj(interp, vobj[j], values_ + j)!=TCL_OK) {
+            for (j = 0; j < _nValues; j++) {
+                if (GetFloatFromObj(interp, vobj[j], _values + j)!=TCL_OK) {
                     return TCL_ERROR;
 		}
 	    }
         } else if ((c == 'u') && (strcmp(string, "units") == 0)) {
-            vUnits_ = strdup(Tcl_GetString(objv[i+1]));
+            _vUnits = strdup(Tcl_GetString(objv[i+1]));
         } else if ((c == 'e') && (strcmp(string, "extents") == 0)) {
 	    int n;
@@ -325,5 +348,5 @@
                 return TCL_ERROR;
             }
-	    extents_ = n;
+	    _nComponents = n;
         } else if ((c == 'a') && (strcmp(string, "axisorder") == 0)) {
 	    Tcl_Obj **order;
@@ -350,9 +373,9 @@
         }
     }
-    if (values_ == NULL) {
+    if (_values == NULL) {
         Tcl_AppendResult(interp, "missing \"values\" key", (char *)NULL);
         return TCL_ERROR;
     }
-    if (nValues_ != (xNum_ * yNum_ * extents_)) {
+    if (_nValues != (_xNum * _yNum * _nComponents)) {
         Tcl_AppendResult(interp, 
 		"wrong number of values: must be xnum*ynum*extents", 
@@ -361,7 +384,4 @@
     }
     
-    fprintf(stderr, "generating %dx%dx%d = %d points\n", 
-	    xNum_, yNum_, extents_, xNum_ * yNum_ * extents_);
-
 #ifndef notdef
     if ((axis[0] != 1) || (axis[1] != 0)) {
@@ -371,24 +391,232 @@
 	float *data, *dp;
 
-	dp = data = new float[nValues_];
-	for (y = 0; y < yNum_; y++) {
+	dp = data = new float[_nValues];
+	for (y = 0; y < _yNum; y++) {
 	    size_t x;
 
-	    for (x = 0; x < xNum_; x++) {
+	    for (x = 0; x < _xNum; x++) {
 		size_t i, v;
 		    
 		/* Compute the index from the data's described ordering. */
-		i = (y + (yNum_ * x)) * extents_;
-		for(v = 0; v < extents_; v++) {
-		    dp[v] = values_[i+v];
+		i = (y + (_yNum * x)) * _nComponents;
+		for(v = 0; v < _nComponents; v++) {
+		    dp[v] = _values[i+v];
 		}
-		dp += extents_;
-	    }
-	}
-	delete [] values_;
-	values_ = data;
+		dp += _nComponents;
+	    }
+	}
+	delete [] _values;
+	_values = data;
     }
 #endif
-    initialized_ = true;
+    _initialized = true;
     return TCL_OK;
 }
+
+
+bool
+Rappture::Unirect3d::ReadVectorDataFromDx(Rappture::Outcome &result, 
+	size_t length, char *string) 
+{
+    size_t nx, ny, nz, npts;
+    double x0, y0, z0, dx, dy, dz, ddx, ddy, ddz;
+    char *p, *endPtr;
+
+    dx = dy = dz = 0.0;         // Suppress compiler warning.
+    x0 = y0 = z0 = 0.0;		// May not have an origin line.
+    for (p = string, endPtr = p + length; p < endPtr; /*empty*/) {
+	char *line;
+
+	line = getline(&p, endPtr);
+        if (line == endPtr) {
+	    break;
+	}
+        if ((line[0] == '#') || (line == '\0')) {
+	    continue;		// Skip blank or comment lines.
+	}
+	if (sscanf(line, "object %*d class gridpositions counts %d %d %d", 
+		   &nx, &ny, &nz) == 3) {
+	    if ((nx < 0) || (ny < 0) || (nz < 0)) {
+		result.addError("invalid grid size: x=%d, y=%d, z=%d", 
+			nx, ny, nz);
+		return false;
+	    }
+	} else if (sscanf(line, "origin %lg %lg %lg", &x0, &y0, &z0) == 3) {
+	    // found origin
+	} else if (sscanf(line, "delta %lg %lg %lg", &ddx, &ddy, &ddz) == 3) {
+	    // found one of the delta lines
+	    if (ddx != 0.0) {
+		dx = ddx;
+	    } else if (ddy != 0.0) {
+		dy = ddy;
+	    } else if (ddz != 0.0) {
+		dz = ddz;
+	    }
+	} else if (sscanf(line, "object %*d class array type %*s shape 3"
+		" rank 1 items %d data follows", &npts) == 1) {
+	    printf("#points=%d\n", npts);
+	    if (npts != nx*ny*nz) {
+		result.addError("inconsistent data: expected %d points"
+				" but found %d points", nx*ny*nz, npts);
+		return false;
+	    }
+	    break;
+	} else if (sscanf(line, "object %*d class array type %*s rank 0"
+		" times %d data follows", &npts) == 1) {
+	    if (npts != nx*ny*nz) {
+		result.addError("inconsistent data: expected %d points"
+				" but found %d points", nx*ny*nz, npts);
+		return false;
+	    }
+	    break;
+	}
+    }
+    if (npts != nx*ny*nz) {
+	result.addError("inconsistent data: expected %d points"
+			" but found %d points", nx*ny*nz, npts);
+	return false;
+    }
+
+    _initialized = false;
+    _xValueMin = _yValueMin = _zValueMin = FLT_MAX;
+    _xValueMax = _yValueMax = _zValueMax = -FLT_MAX;
+    _xMin = x0, _yMin = y0, _zMin = z0;
+    _xNum = nx, _yNum = ny, _zNum = nz;
+    _xMax = _xMin + dx * _xNum;
+    _yMax = _yMin + dy * _yNum;
+    _zMax = _zMin + dz * _zNum;
+    if (_values != NULL) {
+	delete [] _values;
+    }
+    _values = new float[npts * _nComponents];
+    _nValues = 0;
+    for (size_t ix = 0; ix < _xNum; ix++) {
+	for (size_t iy = 0; iy < _yNum; iy++) {
+	    for (size_t iz = 0; iz < _zNum; iz++) {
+		char *line;
+		if ((p == endPtr) || (_nValues > (size_t)npts)) {
+		    break;
+		}
+		line = getline(&p, endPtr);
+		if ((line[0] == '#') || (line[0] == '\0')) {
+		    continue;	// Skip blank or comment lines.
+		}
+		double vx, vy, vz;
+		if (sscanf(line, "%lg %lg %lg", &vx, &vy, &vz) == 3) {
+		    int nindex = (iz*nx*ny + iy*nx + ix) * 3;
+		    if (vx < _xValueMin) {
+			_xValueMin = vx;
+		    } else if (vx > _xValueMax) {
+			_xValueMax = vx;
+		    }
+		    if (vy < _yValueMin) {
+			_yValueMin = vy;
+		    } else if (vy > _yValueMax) {
+			_yValueMax = vy;
+		    }
+		    if (vz < _zValueMin) {
+			_zValueMin = vz;
+		    } else if (vz > _zValueMax) {
+			_zValueMax = vz;
+		    }
+		    if (nindex >= (npts*3)) {
+			fprintf(stderr, "nindex=%d, npts=%d, z=%d, y=%d, x=%d\n",
+				nindex, npts, iz, iy, ix);
+		    }
+		    _values[nindex] = vx;
+		    _values[nindex+1] = vy;
+		    _values[nindex+2] = vz;
+		    _nValues++;
+		}
+	    }
+	}
+    }
+    // make sure that we read all of the expected points
+    if (_nValues != npts) {
+	result.addError("inconsistent data: expected %d points"
+			" but found %d points", npts, _nValues);
+	delete []  _values;
+	_values = NULL;
+	return false;
+    }
+    _nValues *= _nComponents;
+    _initialized = true;
+    return true;
+}
+
+
+bool
+Rappture::Unirect3d::Resample(Rappture::Outcome &result, int nSamples)
+{
+    Rappture::Mesh1D xgrid(_xMin, _xMax, _xNum);
+    Rappture::Mesh1D ygrid(_yMin, _yMax, _yNum);
+    Rappture::Mesh1D zgrid(_zMin, _zMax, _zNum);
+    Rappture::FieldRect3D xfield(xgrid, ygrid, zgrid);
+    Rappture::FieldRect3D yfield(xgrid, ygrid, zgrid);
+    Rappture::FieldRect3D zfield(xgrid, ygrid, zgrid);
+
+    size_t i, j;
+    for (i = 0, j = 0; i < _nValues; i += _nComponents, j++) {
+	double vx, vy, vz;
+
+	vx = _values[i];
+	vy = _values[i+1];
+	vz = _values[i+2];
+	
+	xfield.define(j, vx);
+	yfield.define(j, vy);
+	zfield.define(j, vz);
+    }
+    // Figure out a good mesh spacing
+    double dx, dy, dz;
+    dx = xfield.rangeMax(Rappture::xaxis) - xfield.rangeMin(Rappture::xaxis);
+    dy = xfield.rangeMax(Rappture::yaxis) - xfield.rangeMin(Rappture::yaxis);
+    dz = xfield.rangeMax(Rappture::zaxis) - xfield.rangeMin(Rappture::zaxis);
+
+    double dmin;
+    dmin = pow((dx*dy*dz)/(nSamples*nSamples*nSamples), 0.333);
+    
+    printf("dx:%lf dy:%lf dz:%lf dmin:%lf\n", dx, dy, dz, dmin);
+
+    /* Recompute new number of points for each axis. */
+    _xNum = (size_t)ceil(dx/dmin);
+    _yNum = (size_t)ceil(dy/dmin);
+    _zNum = (size_t)ceil(dz/dmin);
+    
+#ifndef NV40
+    // must be an even power of 2 for older cards
+    _xNum = (int)pow(2.0, ceil(log10((double)_xNum)/log10(2.0)));
+    _yNum = (int)pow(2.0, ceil(log10((double)_yNum)/log10(2.0)));
+    _zNum = (int)pow(2.0, ceil(log10((double)_zNum)/log10(2.0)));
+#endif
+
+    size_t n = _nComponents * _xNum * _yNum * _zNum;
+    float *data = new float[n];
+    memset(data, 0, sizeof(float) * n);
+    
+    // Generate the uniformly sampled rectangle that we need for a volume
+    float *destPtr = data;
+    for (size_t i = 0; i < _zNum; i++) {
+	double z;
+
+	z = _zMin + (i * dmin);
+	for (size_t j = 0; j < _yNum; j++) {
+	    double y;
+		
+	    y = _yMin + (j * dmin);
+	    for (size_t k = 0; k < _xNum; k++) {
+		double x;
+
+		x = _xMin + (k * dmin);
+		destPtr[0] = xfield.value(x, y, z);
+		destPtr[1] = yfield.value(x, y, z);
+		destPtr[2] = zfield.value(x, y, z);
+	    }
+	}
+    }
+    delete [] _values;
+    _values = data;
+    _nValues = _xNum * _yNum * _zNum * _nComponents;
+    return true;
+}
+
Index: trunk/packages/vizservers/nanovis/Unirect.h
===================================================================
--- trunk/packages/vizservers/nanovis/Unirect.h	(revision 1428)
+++ trunk/packages/vizservers/nanovis/Unirect.h	(revision 1429)
@@ -7,20 +7,18 @@
 
 class Unirect3d {
-    size_t xNum_, yNum_, zNum_;
-    size_t nValues_;
-    int extents_;
-
-    float xMin_, xMax_;
-    float yMin_, yMax_;
-    float zMin_, zMax_;
-    float vMin_, vMax_;
-
-    char *xUnits_;
-    char *yUnits_;
-    char *zUnits_;
-    char *vUnits_;
-
-    float *values_;
-    bool initialized_;
+    size_t _xNum, _yNum, _zNum;
+    size_t _nValues;
+    size_t _nComponents;
+    float _xMin, _xMax, _yMin, _yMax, _zMin, _zMax;
+    float _xValueMin, _xValueMax;
+    float _yValueMin, _yValueMax;
+    float _zValueMin, _zValueMax;
+    char *_xUnits;
+    char *_yUnits;
+    char *_zUnits;
+    char *_vUnits;
+
+    float *_values;
+    bool _initialized;
 
 public:
@@ -28,197 +26,222 @@
 	      float yMin, float yMax, size_t yNum, 
 	      float zMin, float zMax, size_t zNum, 
-	      size_t nValues, float *values) {
-	xMax_ = xMax;
-	xMin_ = xMin;
-	xNum_ = xNum;
-	yMax_ = yMax;
-	yMin_ = yMin;
-	yNum_ = yNum;
-	zMax_ = zMax;
-	zMin_ = zMin;
-	zNum_ = zNum;
-	nValues_ = nValues;
-	values_ = values;
-	initialized_ = true;
-    }
-
-    Unirect3d(void) {
-	values_ = NULL;
-	initialized_ = false;
-	xNum_ = yNum_ = zNum_ = 0;
-	nValues_ = 0;
-	xUnits_ = yUnits_ = zUnits_ = vUnits_ = NULL;
+	      size_t nValues, float *values, size_t nComponents) 
+    {
+	SetValues(xMin, xMax, xNum, yMin, yMax, yNum, zMin, zMax, zNum, 
+		  nValues, values, nComponents);
+    }
+
+    Unirect3d(size_t nComponents = 1) {
+	_values = NULL;
+	_initialized = false;
+	_xNum = _yNum = _zNum = 0;
+	_nValues = 0;
+	_xUnits = _yUnits = _zUnits = _vUnits = NULL;
+	_nComponents = nComponents;
     }
     ~Unirect3d(void) {
-	if (values_ != NULL) {
-	    delete [] values_;
-	}
-	if (xUnits_ != NULL) {
-	    free(xUnits_);
-	}
-	if (yUnits_ != NULL) {
-	    free(yUnits_);
-	}
-	if (zUnits_ != NULL) {
-	    free(zUnits_);
-	}
-	if (vUnits_ != NULL) {
-	    free(vUnits_);
-	}
+	if (_values != NULL) {
+	    delete [] _values;
+	}
+	if (_xUnits != NULL) {
+	    free(_xUnits);
+	}
+	if (_yUnits != NULL) {
+	    free(_yUnits);
+	}
+	if (_zUnits != NULL) {
+	    free(_zUnits);
+	}
+	if (_vUnits != NULL) {
+	    free(_vUnits);
+	}
+    }
+    void SetValues(float xMin, float xMax, size_t xNum, 
+		   float yMin, float yMax, size_t yNum, 
+		   float zMin, float zMax, size_t zNum, 
+		   size_t nValues, float *values, size_t nComponents) {
+	_xMax = xMax;
+	_xMin = xMin;
+	_xNum = xNum;
+	_yMax = yMax;
+	_yMin = yMin;
+	_yNum = yNum;
+	_zMax = zMax;
+	_zMin = zMin;
+	_zNum = zNum;
+	_nValues = nValues;
+	_values = values;
+	_initialized = true;
+	_nComponents = nComponents;
     }
     size_t xNum(void) {
-	return xNum_;
+	return _xNum;
     }
     size_t yNum(void) {
-	return yNum_;
+	return _yNum;
     }
     size_t zNum(void) {
-	return zNum_;
+	return _zNum;
     }
     float xMin(void) {
-	return xMin_;
+	return _xMin;
     }
     float yMin(void) {
-	return yMin_;
+	return _yMin;
     }
     float zMin(void) {
-	return zMin_;
+	return _zMin;
     }
     float xMax(void) {
-	return xMax_;
+	return _xMax;
     }
     float yMax(void) {
-	return yMax_;
+	return _yMax;
     }
     float zMax(void) {
-	return zMax_;
+	return _zMax;
+    }
+    float xValueMin(void) {
+	return _xValueMin;
+    }
+    float yValueMin(void) {
+	return _yValueMin;
+    }
+    float zValueMin(void) {
+	return _zValueMin;
+    }
+    float xValueMax(void) {
+	return _xValueMax;
+    }
+    float yValueMax(void) {
+	return _yValueMax;
+    }
+    float zValueMax(void) {
+	return _zValueMax;
+    }
+    size_t nComponents(void) {
+	return _nComponents;
     }
     const char *xUnits(void) {
-	return xUnits_;
+	return _xUnits;
     }
     const char *yUnits(void) {
-	return yUnits_;
+	return _yUnits;
     }
     const char *zUnits(void) {
-	return zUnits_;
+	return _zUnits;
     }
     const char *vUnits(void) {
-	return vUnits_;
+	return _vUnits;
+    }
+    const float *values(void) {
+	return _values;
+    }
+    const float *SaveValues(void) {
+	float *values;
+	values = _values;
+	_values = NULL;
+	_nValues = 0;
+	return values;
+    }
+    size_t nValues(void) {
+	return _nValues;
+    }
+    int LoadData(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv);
+
+    bool ReadVectorDataFromDx(Rappture::Outcome &result, size_t length, 
+	char *string);
+    bool Resample(Rappture::Outcome &context, int nSamples = 30);
+    bool isInitialized(void) {
+	return _initialized;
+    }
+};
+
+class Unirect2d {
+    size_t _xNum, _yNum;
+    size_t _nValues;
+    size_t _nComponents;
+
+    float _xMin, _xMax;
+    float _yMin, _yMax;
+    float _xValueMin, _xValueMax;
+    float _yValueMin, _yValueMax;
+
+    char *_xUnits;
+    char *_yUnits;
+    char *_vUnits;
+
+    float *_values;
+    bool _initialized;
+
+public:
+    Unirect2d(size_t nComponents = 1) {
+	_values = NULL;
+	_initialized = false;
+	_xNum = _yNum = 0;
+	_nValues = 0;
+	_xUnits = _yUnits = _vUnits = NULL;
+	_nComponents = nComponents;
+    }
+    ~Unirect2d(void) {
+	if (_values != NULL) {
+	    delete [] _values;
+	}
+	if (_xUnits != NULL) {
+	    free(_xUnits);
+	}
+	if (_yUnits != NULL) {
+	    free(_yUnits);
+	}
+	if (_vUnits != NULL) {
+	    free(_vUnits);
+	}
+    }
+    size_t xNum(void) {
+	return _xNum;
+    }
+    size_t yNum(void) {
+	return _yNum;
+    }
+    float xMin(void) {
+	return _xMin;
+    }
+    float yMin(void) {
+	return _yMin;
+    }
+    float xMax(void) {
+	return _xMax;
+    }
+    float yMax(void) {
+	return _yMax;
+    }
+    const char *xUnits(void) {
+	return _xUnits;
+    }
+    const char *yUnits(void) {
+	return _yUnits;
+    }
+    const char *vUnits(void) {
+	return _vUnits;
     }
     float *values(void) {
-	return values_;
-    }
-    float *SaveValues(void) {
+	return _values;
+    }
+    float *acceptValues(void) {
 	float *values;
-	values = values_;
-	values_ = NULL;
-	nValues_ = 0;
+	values = _values;
+	_values = NULL;
+	_nValues = 0;
 	return values;
     }
     size_t nValues(void) {
-	return nValues_;
-    }
-    size_t extents(void) {
-	return extents_;
-    }
-    void extents(size_t extents) {
-	extents_ = extents;
+	return _nValues;
     }
     int LoadData(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv);
     bool isInitialized(void) {
-	return initialized_;
+	return _initialized;
     }
 };
 
-class Unirect2d {
-    size_t xNum_, yNum_;
-    size_t nValues_;
-    size_t extents_;
-
-    float xMin_, xMax_;
-    float yMin_, yMax_;
-    float vMin_, vMax_;
-
-    char *xUnits_;
-    char *yUnits_;
-    char *vUnits_;
-
-    float *values_;
-    bool initialized_;
-
-public:
-    Unirect2d(void) {
-	values_ = NULL;
-	initialized_ = false;
-	xNum_ = yNum_ = 0;
-	nValues_ = 0;
-	xUnits_ = yUnits_ = vUnits_ = NULL;
-    }
-    ~Unirect2d(void) {
-	if (values_ != NULL) {
-	    delete [] values_;
-	}
-	if (xUnits_ != NULL) {
-	    free(xUnits_);
-	}
-	if (yUnits_ != NULL) {
-	    free(yUnits_);
-	}
-	if (vUnits_ != NULL) {
-	    free(vUnits_);
-	}
-    }
-    size_t xNum(void) {
-	return xNum_;
-    }
-    size_t yNum(void) {
-	return yNum_;
-    }
-    float xMin(void) {
-	return xMin_;
-    }
-    float yMin(void) {
-	return yMin_;
-    }
-    float xMax(void) {
-	return xMax_;
-    }
-    float yMax(void) {
-	return yMax_;
-    }
-    const char *xUnits(void) {
-	return xUnits_;
-    }
-    const char *yUnits(void) {
-	return yUnits_;
-    }
-    const char *vUnits(void) {
-	return vUnits_;
-    }
-    float *values(void) {
-	return values_;
-    }
-    float *acceptValues(void) {
-	float *values;
-	values = values_;
-	values_ = NULL;
-	nValues_ = 0;
-	return values;
-    }
-    size_t nValues(void) {
-	return nValues_;
-    }
-    size_t extents(void) {
-	return extents_;
-    }
-    void extents(size_t extents) {
-	extents_ = extents;
-    }
-    int LoadData(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv);
-    bool isInitialized(void) {
-	return initialized_;
-    }
-};
-
 }
 
Index: trunk/packages/vizservers/nanovis/Volume.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/Volume.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/Volume.cpp	(revision 1429)
@@ -49,5 +49,5 @@
     int fcount = width * height * depth * n_components;
     _data = new float[fcount];
-    if (data) {
+    if (data != NULL) {
         Trace("data is copied\n");
         memcpy(_data, data, fcount * sizeof(float));
Index: trunk/packages/vizservers/nanovis/VolumeRenderer.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/VolumeRenderer.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/VolumeRenderer.cpp	(revision 1429)
@@ -89,4 +89,35 @@
 }
 
+/*
+ * FIXME:  This is a good example of the wrong data structures being used.
+ *	   The volumes should be in a list not a vector.  Each list
+ *	   element should contain pointers to both the volume and the
+ *	   transfer function.  The shouldn't be parallel vectors.  
+ * 
+ *	   And there shouldn't be one vector for "all" volumes and another
+ *	   for those being rendered, unless you work to keep them in sync.  
+ *	   Use one master list instead.
+ */
+void
+VolumeRenderer::remove_volume(size_t volIndex)
+{
+    vector<Volume *>::iterator vIter;
+    vector<TransferFunction *>::iterator tfIter;
+
+    assert(volIndex < volume.size());
+    assert(volIndex < tf.size());
+
+    vIter = volume.begin();
+    tfIter = tf.begin();
+    size_t i;
+    for (i = 0; i < volIndex; i++) {
+	tfIter++;
+	vIter++;
+    }
+    volume.erase(vIter);
+    tf.erase(tfIter);
+    n_volumes--;
+}
+
 void
 VolumeRenderer::shade_volume(Volume* _vol, TransferFunction* _tf)
@@ -177,4 +208,7 @@
 
     for(int vol_index = 0; vol_index< num_volumes; vol_index++) {
+	cur_vol = NULL;
+	cur_tf = NULL;
+#ifdef notdef
         if (vol_index != n_volumes) {
             cur_vol = volume[vol_index];
@@ -184,10 +218,15 @@
             cur_tf = ani_tf;
         }
-    
+#else
+	cur_vol = volume[vol_index];
+	cur_tf = tf[vol_index];
+#endif
+
         polys[vol_index] = NULL;
         actual_slices[vol_index] = 0;
 
-        if(!cur_vol->is_enabled())
+        if(!cur_vol->is_enabled()) {
             continue; //skip this volume
+	}
 
         int n_slices = cur_vol->get_n_slice();
Index: trunk/packages/vizservers/nanovis/VolumeRenderer.h
===================================================================
--- trunk/packages/vizservers/nanovis/VolumeRenderer.h	(revision 1428)
+++ trunk/packages/vizservers/nanovis/VolumeRenderer.h	(revision 1429)
@@ -106,4 +106,5 @@
 
     int add_volume(Volume* _vol, TransferFunction* _tf); 
+    void remove_volume(size_t volIndex); 
     // add a volume and its transfer function
     // we require a transfer function when a 
Index: trunk/packages/vizservers/nanovis/config.h
===================================================================
--- trunk/packages/vizservers/nanovis/config.h	(revision 1428)
+++ trunk/packages/vizservers/nanovis/config.h	(revision 1429)
@@ -31,5 +31,5 @@
 #define PROTOTYPE		1
 
-//#define NEW_FLOW_ENGINE
+#define NEW_FLOW_ENGINE		1
 
 /* 
Index: trunk/packages/vizservers/nanovis/dxReader.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/dxReader.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/dxReader.cpp	(revision 1429)
@@ -162,8 +162,9 @@
     }
     return new Rappture::Unirect3d(x0, x0 + dx * nx, nx, y0, y0 + dy * ny, ny,
-				   z0, z0 + dz * nz, nz, nValues, values);
+				   z0, z0 + dz * nz, nz, nValues, values, 3);
 }
 
 
+#ifdef notdef
 /*
  * Load a 3D vector field from a dx-format file
@@ -363,4 +364,5 @@
     return true;
 }
+#endif
 
 /* Load a 3D volume from a dx-format file
@@ -744,5 +746,4 @@
     while (!fin.eof()) {
         fin.getline(line, sizeof(line) - 1);
-	fprintf(stderr, "line is %s\n", line);
         if (fin.fail()) {
             result.error("error in data stream");
@@ -840,6 +841,4 @@
         }
     }
-    fprintf(stderr, "found nx=%d ny=%d, nz=%d, x0=%f, y0=%f, z0=%f\n",
-	    nx, ny, nz, x0, y0, z0);
     // read data points
     if (fin.eof()) {
@@ -869,6 +868,4 @@
 		int nindex = iz*nx*ny + iy*nx + ix;
 		field.define(nindex, dval[p]);
-		fprintf(stderr,"nindex = %i\tdval[%i] = %lg\n", nindex, p,
-                        dval[p]);
 		fflush(stderr);
 		nread++;
@@ -1177,5 +1174,4 @@
     while (!fin.eof()) {
         fin.getline(line, sizeof(line) - 1);
-	fprintf(stderr, "line is %s\n", line);
         if (fin.fail()) {
 	    result.addError("line \"%s\"error in data stream");
@@ -1273,6 +1269,4 @@
     }
 
-    fprintf(stderr, "found nx=%d ny=%d, nz=%d, x0=%f, y0=%f, z0=%f\n",
-	    nx, ny, nz, x0, y0, z0);
     // read data points
     if (fin.eof()) {
Index: trunk/packages/vizservers/nanovis/nanovis.cpp
===================================================================
--- trunk/packages/vizservers/nanovis/nanovis.cpp	(revision 1428)
+++ trunk/packages/vizservers/nanovis/nanovis.cpp	(revision 1429)
@@ -69,4 +69,8 @@
 #include <R2/R2Fonts.h>
 
+#include "Unirect.h"
+#include "Switch.h"
+#include "FlowCmd.h"
+
 #define SIZEOF_BMP_HEADER   54
 
@@ -126,13 +130,7 @@
 FILE *NanoVis::recfile = NULL;
 
-bool NanoVis::lic_on = false;
-bool NanoVis::particle_on = false;
-bool NanoVis::vector_on = false;
 bool NanoVis::axis_on = true;
 bool NanoVis::config_pending = false;
 bool NanoVis::debug_flag = false;
-bool NanoVis::lic_slice_x_visible = false;
-bool NanoVis::lic_slice_y_visible = false;
-bool NanoVis::lic_slice_z_visible = false;
 
 Tcl_Interp *NanoVis::interp;
@@ -427,15 +425,18 @@
     int result;
 
+#ifdef notdef
     if (NanoVis::debug_flag) {
         fprintf(stderr, "in ExecuteCommand(%s)\n", Tcl_DStringValue(dsPtr));
     }
-
+#endif
     gettimeofday(&tv, NULL);
     start = CVT2SECS(tv);
 
+#ifdef notdef
     if (NanoVis::logfile != NULL) {
         fprintf(NanoVis::logfile, "%s", Tcl_DStringValue(dsPtr));
         fflush(NanoVis::logfile);
     }
+#endif
     if (NanoVis::recfile != NULL) {
         fprintf(NanoVis::recfile, "%s", Tcl_DStringValue(dsPtr));
@@ -450,7 +451,9 @@
     stats.cmdTime += finish - start;
     stats.nCommands++;
+#ifdef notdef
     if (NanoVis::debug_flag) {
         fprintf(stderr, "leaving ExecuteCommand status=%d\n", result);
     }
+#endif
     return result;
 }
@@ -506,4 +509,5 @@
     volume[index] = new Volume(0.f, 0.f, 0.f, width, height, depth, 1.,
 			       n_component, data, vmin, vmax, nzero_min);
+    fprintf(stderr, "VOLINDEX=%d, n_volumes=%d\n", index, n_volumes);
     return volume[index];
 }
@@ -665,6 +669,6 @@
     }
     
-    if (screen_buffer) {
-        delete[] screen_buffer;
+    if (screen_buffer != NULL) {
+        delete [] screen_buffer;
         screen_buffer = NULL;
     }
@@ -827,7 +831,7 @@
 
 
-#if PROTOTYPE
-    licRenderer = new NvLIC(NMESH, NPIX, NPIX, lic_axis, Vector3(lic_slice_x, lic_slice_y, lic_slice_z), g_context);
-#endif
+    licRenderer = new NvLIC(NMESH, NPIX, NPIX, lic_axis, 
+			    Vector3(lic_slice_x, lic_slice_y, lic_slice_z), 
+			    g_context);
 
     ImageLoaderFactory::getInstance()->addLoaderImpl("bmp", new BMPImageLoaderImpl());
@@ -835,5 +839,7 @@
     grid->setFont(fonts);
 
+#ifdef notdef
     pointset_renderer = new PointSetRenderer();
+#endif
 }
 
@@ -1041,5 +1047,5 @@
             sprintf(filename, "/tmp/flow_animation/image%03d.bmp", frame_number);
 
-        printf("Writing %s\n", filename);
+        fprintf(stderr, "Writing %s\n", filename);
         f = fopen(filename, "wb");
         if (f == 0) {
@@ -1052,9 +1058,10 @@
         }
     }
-    ssize_t nWritten;
-    nWritten = fwrite(header, SIZEOF_BMP_HEADER, 1, f);
-    assert(nWritten == SIZEOF_BMP_HEADER);
-    nWritten = fwrite(screen_buffer, (3*win_width+pad)*win_height, 1, f);
-    assert(nWritten == (3*win_width+pad)*win_height);
+    if (fwrite(header, SIZEOF_BMP_HEADER, 1, f) != 1) {
+	Trace("can't write header: short write\n");
+    }
+    if (fwrite(screen_buffer, (3*win_width+pad)*win_height, 1, f) != 1) {
+	Trace("can't write data: short write\n");
+    }
     fclose(f);
 }
@@ -1633,4 +1640,8 @@
         fprintf(stderr, "in display\n");
     }
+
+    if (FlowCmd::flags & FlowCmd::MAP_PENDING) {
+	FlowCmd::MapFlows();
+    }
     //assert(glGetError()==0);
     if (HeightMap::update_pending) {
@@ -1701,8 +1712,13 @@
         if ((licRenderer != NULL) && (licRenderer->isActivated())) {
             licRenderer->render();
-        }
-
+	    /*FlowCmd::SetupFlows();*/
+        }
+#ifdef notdef
         if ((flowVisRenderer != NULL) && (flowVisRenderer->isActivated())) {
             flowVisRenderer->render();
+	}
+#endif
+	if (FlowCmd::flags & FlowCmd::REDRAW_PENDING) {
+	    FlowCmd::RenderFlows();
 	}
 
@@ -1732,9 +1748,9 @@
         perf->disable();
     }
-
     perf->reset();
     if (debug_flag) {
         fprintf(stderr, "leaving display\n");
     }
+
 
 }
@@ -1923,6 +1939,6 @@
 		break;
 	case '5' :
-		{
-			printf("vector field deleted (vf_name2)\n");
+86		{
+a			printf("vector field deleted (vf_name2)\n");
 			NanoVis::flowVisRenderer->removeVectorField("vf_name2");
 		}
@@ -2063,9 +2079,8 @@
     int status = TCL_OK;
 
-    //
     //  Read and execute as many commands as we can from stdin...
-    //
+
     bool isComplete = false;
-    while (status == TCL_OK) {
+    while ((!feof(NanoVis::stdin)) && (status == TCL_OK)) {
         //
         //  Read the next command from the buffer.  First time through we
@@ -2088,5 +2103,5 @@
                     break;
                 }
-                DoExit(0);
+		DoExit(100);
             }
             ch = (char)c;
@@ -2103,5 +2118,4 @@
             break;
         }
-
         if (isComplete) {
             // back to original flags during command evaluation...
@@ -2167,5 +2181,5 @@
 #endif
     if (feof(NanoVis::stdin)) {
-        DoExit(0);
+        DoExit(90);
     }
     if (debug_flag) {
@@ -2336,5 +2350,5 @@
 
     glutMainLoop();
-    DoExit(0);
+    DoExit(80);
 }
 
Index: trunk/packages/vizservers/nanovis/nanovis.h
===================================================================
--- trunk/packages/vizservers/nanovis/nanovis.h	(revision 1428)
+++ trunk/packages/vizservers/nanovis/nanovis.h	(revision 1429)
@@ -137,7 +137,4 @@
     static float lic_slice_y;
     static float lic_slice_z;
-    static bool lic_slice_x_visible;
-    static bool lic_slice_y_visible;
-    static bool lic_slice_z_visible;
     static int lic_axis;	/* 0:x, 1:y, 2:z */
 
@@ -149,7 +146,4 @@
 
     static bool debug_flag;
-    static bool lic_on;
-    static bool particle_on;
-    static bool vector_on;
 
     static Tcl_Interp *interp;
