source: trunk/packages/vizservers/nanovis/FlowCmd.cpp @ 1456

Last change on this file since 1456 was 1456, checked in by gah, 15 years ago
File size: 53.5 KB
Line 
1
2#include <assert.h>
3#include <stdlib.h>
4#include <stddef.h>
5#include <limits.h>
6#include <tcl.h>
7#include "Switch.h"
8#include <RpField1D.h>
9#include <RpFieldRect3D.h>
10#include <RpFieldPrism3D.h>
11#include <RpOutcome.h>
12#include <RpAVTranslate.h>
13#include "Trace.h"
14#include "TransferFunction.h"
15
16#include "nanovis.h"
17#include "Command.h"
18#include "CmdProc.h"
19#include "Nv.h"
20
21#include "NvLIC.h"
22
23#include "Unirect.h"
24#include "FlowCmd.h"
25
26#define RELPOS 0
27#define ABSPOS 1
28
29static Rappture::SwitchParseProc AxisSwitchProc;
30static Rappture::SwitchCustom axisSwitch = {
31    AxisSwitchProc, NULL, 0,
32};
33
34static Rappture::SwitchParseProc ColorSwitchProc;
35static Rappture::SwitchCustom colorSwitch = {
36    ColorSwitchProc, NULL, 0,
37};
38
39static Rappture::SwitchParseProc PointSwitchProc;
40static Rappture::SwitchCustom pointSwitch = {
41    PointSwitchProc, NULL, 0,
42};
43
44static Rappture::SwitchParseProc PositionSwitchProc;
45static Rappture::SwitchCustom positionSwitch = {
46    PositionSwitchProc, NULL, 0,
47};
48
49static Rappture::SwitchParseProc TransferFunctionSwitchProc;
50static Rappture::SwitchCustom transferFunctionSwitch = {
51    TransferFunctionSwitchProc, NULL, 0,
52};
53
54Rappture::SwitchSpec FlowCmd::_switches[] = {
55    {Rappture::SWITCH_BOOLEAN, "-slice", "boolean",
56        offsetof(FlowValues, sliceVisible), 0},
57    {Rappture::SWITCH_CUSTOM, "-axis", "axis",
58        offsetof(FlowValues, slicePos.axis), 0, 0, &axisSwitch},
59    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
60        offsetof(FlowValues, isHidden), 0},
61    {Rappture::SWITCH_CUSTOM, "-position", "number",
62        offsetof(FlowValues, slicePos), 0, 0, &positionSwitch},
63    {Rappture::SWITCH_CUSTOM, "-transferfunction", "name",
64        offsetof(FlowValues, tfPtr), 0, 0, &transferFunctionSwitch},
65    {Rappture::SWITCH_BOOLEAN, "-volume", "boolean",
66        offsetof(FlowValues, showVolume), 0},
67    {Rappture::SWITCH_BOOLEAN, "-outline", "boolean",
68        offsetof(FlowValues, showOutline), 0},
69    {Rappture::SWITCH_END}
70};
71
72Rappture::SwitchSpec FlowParticles::_switches[] = {
73    {Rappture::SWITCH_CUSTOM, "-axis", "string",
74     offsetof(FlowParticlesValues, position.axis), 0, 0, &axisSwitch},
75    {Rappture::SWITCH_CUSTOM, "-color", "{r g b a}",
76     offsetof(FlowParticlesValues, color), 0, 0,  &colorSwitch},
77    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
78     offsetof(FlowParticlesValues, isHidden), 0},
79    {Rappture::SWITCH_CUSTOM, "-position", "number",
80        offsetof(FlowValues, slicePos), 0, 0, &positionSwitch},
81    {Rappture::SWITCH_END}
82};
83
84Rappture::SwitchSpec FlowBox::_switches[] = {
85    {Rappture::SWITCH_CUSTOM, "-color", "{r g b a}",
86     offsetof(FlowBoxValues, color), 0, 0,  &colorSwitch},
87    {Rappture::SWITCH_CUSTOM, "-corner1", "{x y z}",
88     offsetof(FlowBoxValues, corner1), 0, 0, &pointSwitch},
89    {Rappture::SWITCH_CUSTOM, "-corner2", "{x y z}",
90     offsetof(FlowBoxValues, corner2), 0, 0, &pointSwitch},
91    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
92     offsetof(FlowBoxValues, isHidden), 0},
93    {Rappture::SWITCH_FLOAT, "-linewidth", "number",
94     offsetof(FlowBoxValues, lineWidth), 0},
95    {Rappture::SWITCH_END}
96};
97
98
99static Tcl_ObjCmdProc FlowInstObjCmd;
100static Tcl_CmdDeleteProc FlowInstDeleteProc;
101
102
103FlowParticles::FlowParticles(const char *name, Tcl_HashEntry *hPtr)
104{
105    memset(this, 0, sizeof(FlowParticles));
106    _name = name;
107    _hashPtr = hPtr;
108    _sv.isHidden = false;
109    _sv.position.axis = 0;              /* X_AXIS */
110    _sv.position.value = 0.0f; 
111    _sv.position.flags = RELPOS;
112    _sv.color.r = _sv.color.b = _sv.color.g = _sv.color.a = 1.0f;
113    _rendererPtr = new NvParticleRenderer(NMESH, NMESH,
114                /* Global nVidia Cg context */g_context);
115}
116
117void
118FlowParticles::Render(void)
119{
120    Trace("rendering particles %s\n", _name);
121    Trace("rendering particles %s axis=%d\n", _name, _sv.position.axis);
122    Trace("rendering particles %s position=%g\n", _name, sv.position.value);
123    Trace("rendering particles %s position=%g\n", _name,
124          FlowCmd::GetRelativePosition(&_sv.position));
125
126    _rendererPtr->setPos(FlowCmd::GetRelativePosition(&_sv.position));
127    _rendererPtr->setAxis(_sv.position.axis);
128    assert(_rendererPtr->active());
129    _rendererPtr->render();
130}
131
132void
133FlowParticles::Configure(void)
134{
135    _rendererPtr->setPos(FlowCmd::GetRelativePosition(&_sv.position));
136    _rendererPtr->setColor(Vector4(_sv.color.r, _sv.color.g, _sv.color.b,
137                _sv.color.a));
138    _rendererPtr->setAxis(_sv.position.axis);
139    _rendererPtr->active(!_sv.isHidden);
140}
141
142FlowBox::FlowBox(const char *name, Tcl_HashEntry *hPtr)
143{
144    _name = name;
145    _hashPtr = hPtr;
146    _sv.isHidden = false;
147    _sv.corner1.x = 0.0f;       
148    _sv.corner1.y = 0.0f;       
149    _sv.corner1.z = 0.0f;       
150    _sv.corner2.x = 1.0f;       
151    _sv.corner2.y = 1.0f;       
152    _sv.corner2.z = 1.0f;       
153    _sv.lineWidth = 1.2f;
154    _sv.color.r = _sv.color.b = _sv.color.g = _sv.color.a = 1.0f;
155
156}
157
158void
159FlowBox::Render(Volume *volPtr)
160{
161    Trace("rendering boxes %s\n", _name);
162    if ((_sv.corner1.x == _sv.corner2.x) ||
163        (_sv.corner1.y == _sv.corner2.y) ||
164        (_sv.corner1.z == _sv.corner2.z)) {
165        return;                         
166    }
167    Trace("rendering boxes %s\n", _name);
168    glColor4d(_sv.color.r, _sv.color.g, _sv.color.b, _sv.color.a);
169
170    glPushMatrix();
171
172    glEnable(GL_DEPTH_TEST);
173    glDisable(GL_TEXTURE_2D);
174    glEnable(GL_BLEND);
175   
176    glPushMatrix();
177    Vector3 *originPtr = volPtr->get_location();
178    glTranslatef(originPtr->x, originPtr->y, originPtr->z);
179
180    double sx, sy, sz;
181    sx = 1.0;
182    sy = volPtr->height / (double)volPtr->width;
183    sz = volPtr->depth  / (double)volPtr->width;
184    glScaled(sx, sy, sz);
185
186    Vector3 min, max;
187
188    min = volPtr->getPhysicalBBoxMin();
189    max = volPtr->getPhysicalBBoxMax();
190
191    float x0, y0, z0, x1, y1, z1;
192    x0 = (_sv.corner1.x - min.x) / (max.x - min.x);
193    y0 = (_sv.corner1.y - min.y) / (max.y - min.y);
194    z0 = (_sv.corner1.z - min.z) / (max.z - min.z);
195    x1 = (_sv.corner2.x - min.x) / (max.x - min.x);
196    y1 = (_sv.corner2.y - min.y) / (max.y - min.y);
197    z1 = (_sv.corner2.z - min.z) / (max.z - min.z);
198   
199    Trace("rendering box %g,%g %g,%g %g,%g\n", x0, x1, y0, y1, z0, z1);
200
201    glLineWidth(_sv.lineWidth);
202    glBegin(GL_LINE_LOOP);
203    {
204        glVertex3d(x0, y0, z0);
205        glVertex3d(x1, y0, z0);
206        glVertex3d(x1, y1, z0);
207        glVertex3d(x0, y1, z0);
208    }
209    glEnd();
210    glBegin(GL_LINE_LOOP);
211    {
212        glVertex3d(x0, y0, z1);
213        glVertex3d(x1, y0, z1);
214        glVertex3d(x1, y1, z1);
215        glVertex3d(x0, y1, z1);
216    }
217    glEnd();
218   
219    glBegin(GL_LINE_LOOP);
220    {
221        glVertex3d(x0, y0, z0);
222        glVertex3d(x0, y0, z1);
223        glVertex3d(x0, y1, z1);
224        glVertex3d(x0, y1, z0);
225    }
226    glEnd();
227   
228    glBegin(GL_LINE_LOOP);
229    {
230        glVertex3d(x1, y0, z0);
231        glVertex3d(x1, y0, z1);
232        glVertex3d(x1, y1, z1);
233        glVertex3d(x1, y1, z0);
234    }
235    glEnd();
236
237    glPopMatrix();
238    assert(CheckGL(AT));
239    glPopMatrix();
240    assert(CheckGL(AT));
241
242    glDisable(GL_DEPTH_TEST);
243    glDisable(GL_BLEND);
244    glEnable(GL_TEXTURE_2D);
245}
246
247
248FlowCmd::FlowCmd(Tcl_Interp *interp, const char *name, Tcl_HashEntry *hPtr)
249{
250    memset(this, 0, sizeof(FlowCmd));
251    _name = name;
252    _interp = interp;
253    _hashPtr = hPtr;
254    _volIndex = -1;                     /* Indicates that no volume slot has
255                                         * been allocated for this vector. */
256    _sv.sliceVisible = 1;
257    _volPtr = NULL;
258    _cmdToken = Tcl_CreateObjCommand(interp, (char *)_name,
259        (Tcl_ObjCmdProc *)FlowInstObjCmd, this, FlowInstDeleteProc);
260    Tcl_InitHashTable(&_particlesTable, TCL_STRING_KEYS);
261    Tcl_InitHashTable(&_boxTable, TCL_STRING_KEYS);
262}
263
264FlowCmd::~FlowCmd(void)
265{
266    Rappture::FreeSwitches(_switches, &_sv, 0);
267    if (_hashPtr != NULL) {
268        Tcl_DeleteHashEntry(_hashPtr);
269    }
270    if (_fieldPtr != NULL) {
271        delete _fieldPtr;
272    }
273    if (_dataPtr != NULL) {
274        delete _dataPtr;
275    }
276     if (_volPtr != NULL) {
277        delete _volPtr;
278        _volPtr = NULL;
279        NanoVis::volume[_volIndex] = NULL;
280        NanoVis::vol_renderer->remove_volume(_volIndex);
281    }
282
283    FlowBox *boxPtr;
284    FlowBoxIterator boxIter;
285    for (boxPtr = FirstBox(&boxIter); boxPtr != NULL;
286         boxPtr = NextBox(&boxIter)) {
287        boxPtr->disconnect();
288        delete boxPtr;
289    }   
290    FlowParticles *particlesPtr;
291    FlowParticlesIterator partIter;
292    for (particlesPtr = FirstParticles(&partIter); particlesPtr != NULL;
293         particlesPtr = NextParticles(&partIter)) {
294        particlesPtr->disconnect();
295        delete particlesPtr;
296    }   
297    Tcl_DeleteHashTable(&_particlesTable);
298    Tcl_DeleteHashTable(&_boxTable);
299}
300
301void
302FlowCmd::ResetParticles(void)
303{
304    FlowParticlesIterator iter;
305    FlowParticles *particlesPtr;
306    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL;
307         particlesPtr = NextParticles(&iter)) {
308        particlesPtr->Reset();
309    }
310}
311
312void
313FlowCmd::Advect(void)
314{
315    NvVectorField *fieldPtr;
316    fieldPtr = VectorField();
317    fieldPtr->active(true);
318    FlowParticlesIterator iter;
319    FlowParticles *particlesPtr;
320    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL;
321         particlesPtr = NextParticles(&iter)) {
322        if (particlesPtr->visible()) {
323            particlesPtr->Advect();
324        }
325    }
326}
327
328void
329FlowCmd::Render(void)
330{
331    _fieldPtr->active(true);
332    _fieldPtr->render();
333    FlowParticlesIterator iter;
334    FlowParticles *particlesPtr;
335    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL;
336         particlesPtr = NextParticles(&iter)) {
337        if (particlesPtr->visible()) {
338            particlesPtr->Render();
339        }
340    }
341    Trace("in Render before boxes %s\n", _name);
342    RenderBoxes();
343}
344
345int
346FlowCmd::CreateParticles(Tcl_Interp *interp, Tcl_Obj *objPtr)
347{
348    Tcl_HashEntry *hPtr;
349    int isNew;
350    const char *particlesName = Tcl_GetString(objPtr);
351    hPtr = Tcl_CreateHashEntry(&_particlesTable, particlesName, &isNew);
352    if (!isNew) {
353        Tcl_AppendResult(interp, "particle injection plane \"",
354                         particlesName, "\" already exists.", (char *)NULL);
355        return TCL_ERROR;
356    }
357    particlesName = Tcl_GetHashKey(&_particlesTable, hPtr);
358    FlowParticles *particlesPtr;
359    particlesPtr = new FlowParticles(particlesName, hPtr);
360    if (particlesPtr == NULL) {
361        Tcl_AppendResult(interp, "can't allocate particle injection plane",
362                (char *)NULL);
363        Tcl_DeleteHashEntry(hPtr);
364        return TCL_ERROR;
365    }
366    Tcl_SetHashValue(hPtr, particlesPtr);
367    return TCL_OK;
368}
369
370int
371FlowCmd::GetParticles(Tcl_Interp *interp, Tcl_Obj *objPtr,
372                      FlowParticles **particlesPtrPtr)
373{
374    Tcl_HashEntry *hPtr;
375    hPtr = Tcl_FindHashEntry(&_particlesTable, Tcl_GetString(objPtr));
376    if (hPtr == NULL) {
377        if (interp != NULL) {
378            Tcl_AppendResult(interp, "can't find a particle injection plane \"",
379                         Tcl_GetString(objPtr), "\"", (char *)NULL);
380        }
381        return TCL_ERROR;
382    }
383    *particlesPtrPtr = (FlowParticles *)Tcl_GetHashValue(hPtr);
384    return TCL_OK;
385}
386
387FlowParticles *
388FlowCmd::FirstParticles(FlowParticlesIterator *iterPtr)
389{
390    iterPtr->hashPtr = Tcl_FirstHashEntry(&_particlesTable,
391        &iterPtr->hashSearch);
392    if (iterPtr->hashPtr == NULL) {
393        return NULL;
394    }
395    return (FlowParticles *)Tcl_GetHashValue(iterPtr->hashPtr);
396}
397
398FlowParticles *
399FlowCmd::NextParticles(FlowParticlesIterator *iterPtr)
400{
401    if (iterPtr->hashPtr == NULL) {
402        return NULL;
403    }
404    iterPtr->hashPtr = Tcl_NextHashEntry(&iterPtr->hashSearch);
405    if (iterPtr->hashPtr == NULL) {
406        return NULL;
407    }
408    return (FlowParticles *)Tcl_GetHashValue(iterPtr->hashPtr);
409}
410
411int
412FlowCmd::CreateBox(Tcl_Interp *interp, Tcl_Obj *objPtr)
413{
414    Tcl_HashEntry *hPtr;
415    int isNew;
416    hPtr = Tcl_CreateHashEntry(&_boxTable, Tcl_GetString(objPtr), &isNew);
417    if (!isNew) {
418        Tcl_AppendResult(interp, "box \"", Tcl_GetString(objPtr),
419                "\" already exists in flow \"", name(), "\"", (char *)NULL);
420        return TCL_ERROR;
421    }
422    const char *boxName;
423    boxName = Tcl_GetHashKey(&_boxTable, hPtr);
424    FlowBox *boxPtr;
425    boxPtr = new FlowBox(boxName, hPtr);
426    if (boxPtr == NULL) {
427        Tcl_AppendResult(interp, "can't allocate box \"", boxName, "\"",
428                         (char *)NULL);
429        Tcl_DeleteHashEntry(hPtr);
430        return TCL_ERROR;
431    }
432    Tcl_SetHashValue(hPtr, boxPtr);
433    return TCL_OK;
434}
435
436int
437FlowCmd::GetBox(Tcl_Interp *interp, Tcl_Obj *objPtr, FlowBox **boxPtrPtr)
438{
439    Tcl_HashEntry *hPtr;
440    hPtr = Tcl_FindHashEntry(&_boxTable, Tcl_GetString(objPtr));
441    if (hPtr == NULL) {
442        if (interp != NULL) {
443            Tcl_AppendResult(interp, "can't find a box \"",
444                Tcl_GetString(objPtr), "\" in flow \"", name(), "\"",
445                (char *)NULL);
446        }
447        return TCL_ERROR;
448    }
449    *boxPtrPtr = (FlowBox *)Tcl_GetHashValue(hPtr);
450    return TCL_OK;
451}
452
453FlowBox *
454FlowCmd::FirstBox(FlowBoxIterator *iterPtr)
455{
456    iterPtr->hashPtr = Tcl_FirstHashEntry(&_boxTable, &iterPtr->hashSearch);
457    if (iterPtr->hashPtr == NULL) {
458        return NULL;
459    }
460    return (FlowBox *)Tcl_GetHashValue(iterPtr->hashPtr);
461}
462
463FlowBox *
464FlowCmd::NextBox(FlowBoxIterator *iterPtr)
465{
466    if (iterPtr->hashPtr == NULL) {
467        return NULL;
468    }
469    iterPtr->hashPtr = Tcl_NextHashEntry(&iterPtr->hashSearch);
470    if (iterPtr->hashPtr == NULL) {
471        return NULL;
472    }
473    return (FlowBox *)Tcl_GetHashValue(iterPtr->hashPtr);
474}
475
476
477void
478FlowCmd::InitVectorField(void)
479{
480    if (_volPtr != NULL) {
481        delete _volPtr;
482        _volPtr = NULL;
483        NanoVis::volume[_volIndex] = NULL;
484        NanoVis::vol_renderer->remove_volume(_volIndex);
485    }
486    // Remove the associated vector field.
487    if (_fieldPtr != NULL) {
488        delete _fieldPtr;
489        _fieldPtr = NULL;
490    }
491   
492}
493
494void
495FlowCmd::InitializeParticles(void)
496{
497    FlowParticles *particlesPtr;
498    FlowParticlesIterator iter;
499    for (particlesPtr = FirstParticles(&iter); particlesPtr != NULL;
500         particlesPtr = NextParticles(&iter)) {
501        particlesPtr->Initialize();
502    }   
503}
504
505bool
506FlowCmd::ScaleVectorField()
507{
508    if (_volPtr != NULL) {
509        delete _volPtr;
510        _volPtr = NULL;
511        NanoVis::volume[_volIndex] = NULL;
512        NanoVis::vol_renderer->remove_volume(_volIndex);
513    }
514    float *vdata;
515    vdata = GetScaledVector();
516    if (vdata == NULL) {
517        return false;
518    }
519    Volume *volPtr;
520    volPtr = MakeVolume(vdata);
521    delete [] vdata;
522    if (volPtr == NULL) {
523        return false;
524    }
525    _volPtr = volPtr;
526
527    _fieldPtr = new NvVectorField();
528    if (_fieldPtr == NULL) {
529        return false;
530    }
531
532    double width, height, depth;
533    width  = NanoVis::xMax - NanoVis::xMin;
534    height = NanoVis::yMax - NanoVis::yMin;
535    depth  = NanoVis::zMax - NanoVis::zMin;
536
537    Vector3 *locationPtr = _volPtr->get_location();
538    /*This is wrong. Need to compute origin. */
539    NanoVis::xOrigin = locationPtr->x;
540    NanoVis::yOrigin = locationPtr->y;
541    NanoVis::zOrigin = locationPtr->z;
542
543    _fieldPtr->setVectorField(_volPtr, *locationPtr,
544        1.0f, height / width, depth  / width, NanoVis::magMax);
545
546    if (NanoVis::licRenderer != NULL) {
547        NanoVis::licRenderer->setVectorField(_volPtr->id,
548                *locationPtr,
549                1.0f / _volPtr->aspect_ratio_width,
550                1.0f / _volPtr->aspect_ratio_height,
551                1.0f / _volPtr->aspect_ratio_depth,
552                _volPtr->wAxis.max());
553        SetCurrentPosition();
554        SetAxis();
555        SetActive();
556    }
557    FlowParticles *particlesPtr;
558    FlowParticlesIterator partIter;
559    for (particlesPtr = FirstParticles(&partIter); particlesPtr != NULL;
560         particlesPtr = NextParticles(&partIter)) {
561        particlesPtr->SetVectorField(_volPtr);
562    }   
563    return true;
564}
565
566void
567FlowCmd::RenderBoxes(void)
568{
569    FlowBoxIterator iter;
570    FlowBox *boxPtr;
571    for (boxPtr = FirstBox(&iter); boxPtr != NULL; boxPtr = NextBox(&iter)) {
572        if (boxPtr->visible()) {
573            boxPtr->Render(_volPtr);
574        }
575    }
576}
577
578float *
579FlowCmd::GetScaledVector(void)
580{
581    assert(_dataPtr->nComponents() == 3);
582    size_t n = _dataPtr->nValues() / _dataPtr->nComponents() * 4;
583    float *data = new float[n];
584    if (data == NULL) {
585        return NULL;
586    }
587    memset(data, 0, sizeof(float) * n);
588    float *destPtr = data;
589    const float *values = _dataPtr->values();
590    for (size_t iz=0; iz < _dataPtr->zNum(); iz++) {
591        for (size_t iy=0; iy < _dataPtr->yNum(); iy++) {
592            for (size_t ix=0; ix < _dataPtr->xNum(); ix++) {
593                double vx, vy, vz, vm;
594                vx = values[0];
595                vy = values[1];
596                vz = values[2];
597                vm = sqrt(vx*vx + vy*vy + vz*vz);
598                destPtr[0] = vm / NanoVis::magMax;
599                destPtr[1] = vx /(2.0*NanoVis::magMax) + 0.5;
600                destPtr[2] = vy /(2.0*NanoVis::magMax) + 0.5;
601                destPtr[3] = vz /(2.0*NanoVis::magMax) + 0.5;
602                values += 3;
603                destPtr += 4;
604            }
605        }
606    }
607    return data;
608}
609
610Volume *
611FlowCmd::MakeVolume(float *data)
612{
613    if (_volIndex < 0) {
614        _volIndex = NanoVis::n_volumes;
615        Trace("VolumeIndex is %d\n", _volIndex);
616    }
617    Volume *volPtr;
618    volPtr = NanoVis::load_volume(_volIndex, _dataPtr->xNum(),
619        _dataPtr->yNum(), _dataPtr->zNum(), 4, data,
620        NanoVis::magMin, NanoVis::magMax, 0);
621    volPtr->xAxis.SetRange(_dataPtr->xMin(), _dataPtr->xMax());
622    volPtr->yAxis.SetRange(_dataPtr->yMin(), _dataPtr->yMax());
623    volPtr->zAxis.SetRange(_dataPtr->zMin(), _dataPtr->zMax());
624    volPtr->wAxis.SetRange(NanoVis::magMin, NanoVis::magMax);
625
626    /*volPtr->update_pending = false;*/
627    Vector3 physicalMin(NanoVis::xMin, NanoVis::yMin, NanoVis::zMin);
628    Vector3 physicalMax(NanoVis::xMax, NanoVis::yMax, NanoVis::zMax);
629    volPtr->setPhysicalBBox(physicalMin, physicalMax);
630
631    volPtr->set_n_slice(256 - _volIndex);
632    // volPtr->set_n_slice(512- _volIndex);
633    volPtr->disable_cutplane(0);
634    volPtr->disable_cutplane(1);
635    volPtr->disable_cutplane(2);
636
637    TransferFunction *tfPtr;
638    tfPtr = _sv.tfPtr;
639    if (tfPtr == NULL) {
640        tfPtr = NanoVis::get_transfunc("default");
641    }
642    NanoVis::vol_renderer->add_volume(volPtr, tfPtr);
643    if (_sv.showVolume) {
644        volPtr->enable_data();
645    } else {
646        volPtr->disable_data();
647    }
648    if (_sv.showOutline) {
649        volPtr->enable_outline();
650    } else {
651        volPtr->disable_outline();
652    }
653    float dx0 = -0.5;
654    float dy0 = -0.5*volPtr->height/volPtr->width;
655    float dz0 = -0.5*volPtr->depth/volPtr->width;
656    volPtr->move(Vector3(dx0, dy0, dz0));
657    return volPtr;
658}
659
660static int
661FlowDataFileOp(ClientData clientData, Tcl_Interp *interp, int objc,
662               Tcl_Obj *const *objv)
663{
664    Rappture::Outcome result;
665   
666    const char *fileName;
667    fileName = Tcl_GetString(objv[3]);
668    Trace("Flow loading data from file %s\n", fileName);
669
670    int nComponents;
671    if (Tcl_GetIntFromObj(interp, objv[4], &nComponents) != TCL_OK) {
672        return TCL_ERROR;
673    }
674    if ((nComponents < 1) || (nComponents > 4)) {
675        Tcl_AppendResult(interp, "bad # of components \"",
676                         Tcl_GetString(objv[4]), "\"", (char *)NULL);
677        return TCL_ERROR;
678    }
679    Rappture::Buffer buf;
680    if (!buf.load(result, fileName)) {
681        Tcl_AppendResult(interp, "can't load data from \"", fileName, "\": ",
682                         result.remark(), (char *)NULL);
683        return TCL_ERROR;
684    }
685
686    FlowCmd *flowPtr = (FlowCmd *)clientData;
687    size_t length = buf.size();
688    char *bytes = (char *)buf.bytes();
689    if ((length > 4) && (strncmp(bytes, "<DX>", 4) == 0)) {
690        Rappture::Unirect3d *dataPtr;
691
692        dataPtr = new Rappture::Unirect3d(nComponents);
693        if (!dataPtr->ImportDx(result, nComponents, buf.size(),
694                (char *)buf.bytes())) {
695            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
696            delete dataPtr;
697            return TCL_ERROR;
698        }
699        flowPtr->SetData(dataPtr);
700    } else if ((length > 11) && (strncmp(bytes, "<unirect3d>", 11) == 0)) {
701        Rappture::Unirect3d *dataPtr;
702        Tcl_CmdInfo cmdInfo;
703
704        /* Set the clientdata field of the unirect3d command to contain
705         * the local data structure. */
706        dataPtr = new Rappture::Unirect3d(nComponents);
707        if (!Tcl_GetCommandInfo(interp, "unirect3d", &cmdInfo)) {
708            return TCL_ERROR;
709        }
710        cmdInfo.objClientData = (ClientData)dataPtr;   
711        Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
712        if (Tcl_Eval(interp, (const char *)buf.bytes()+11) != TCL_OK) {
713            delete dataPtr;
714            return TCL_ERROR;
715        }
716        if (!dataPtr->isInitialized()) {
717            delete dataPtr;
718            return TCL_ERROR;
719        }
720        flowPtr->SetData(dataPtr);
721    } else if ((length > 11) && (strncmp(bytes, "<unirect2d>", 11) == 0)) {
722        Rappture::Unirect2d *dataPtr;
723        Tcl_CmdInfo cmdInfo;
724
725        /* Set the clientdata field of the unirect3d command to contain
726         * the local data structure. */
727        dataPtr = new Rappture::Unirect2d();
728        if (!Tcl_GetCommandInfo(interp, "unirect2d", &cmdInfo)) {
729            return TCL_ERROR;
730        }
731        cmdInfo.objClientData = (ClientData)dataPtr;   
732        Tcl_SetCommandInfo(interp, "unirect2d", &cmdInfo);
733        if (Tcl_Eval(interp, (const char *)buf.bytes()+11) != TCL_OK) {
734            delete dataPtr;
735            return TCL_ERROR;
736        }
737        if (!dataPtr->isInitialized()) {
738            delete dataPtr;
739            return TCL_ERROR;
740        }
741        Rappture::Unirect3d *d3Ptr = new Rappture::Unirect3d(nComponents);
742        d3Ptr->Convert(dataPtr);
743        flowPtr->SetData(d3Ptr);
744        delete dataPtr;
745    } else {
746        Rappture::Unirect3d *dataPtr;
747
748        fprintf(stderr, "header is %.14s\n", buf.bytes());
749        dataPtr = new Rappture::Unirect3d(nComponents);
750        if (!dataPtr->ImportDx(result, nComponents, buf.size(),
751                (char *)buf.bytes())) {
752            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
753            delete dataPtr;
754            return TCL_ERROR;
755        }
756        flowPtr->SetData(dataPtr);
757    }
758    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
759    return TCL_OK;
760}
761
762/*
763 * $flow data follows nbytes nComponents
764 */
765static int
766FlowDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
767                    Tcl_Obj *const *objv)
768{
769    Rappture::Outcome result;
770
771    Trace("Flow Data Loading\n");
772
773    int nBytes;
774    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
775        Trace("Bad nBytes \"%s\"\n", Tcl_GetString(objv[3]));
776        return TCL_ERROR;
777    }
778    if (nBytes <= 0) {
779        Tcl_AppendResult(interp, "bad # bytes request \"",
780                Tcl_GetString(objv[3]), "\" for \"data follows\"", (char *)NULL);
781        Trace("Bad nbytes %d\n", nBytes);
782        return TCL_ERROR;
783    }
784    int nComponents;
785    if (Tcl_GetIntFromObj(interp, objv[4], &nComponents) != TCL_OK) {
786        Trace("Bad # of components \"%s\"\n", Tcl_GetString(objv[4]));
787        return TCL_ERROR;
788    }
789    if (nComponents <= 0) {
790        Tcl_AppendResult(interp, "bad # of components request \"",
791                Tcl_GetString(objv[4]), "\" for \"data follows\"", (char *)NULL);
792        Trace("Bad # of components %d\n", nComponents);
793        return TCL_ERROR;
794    }
795    Rappture::Buffer buf;
796    Trace("Flow Data Loading %d %d\n", nBytes, nComponents);
797    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
798        return TCL_ERROR;
799    }
800    FlowCmd *flowPtr = (FlowCmd *)clientData;
801    size_t length = buf.size();
802    char *bytes = (char *)buf.bytes();
803    if ((length > 4) && (strncmp(bytes, "<DX>", 4) == 0)) {
804        Rappture::Unirect3d *dataPtr;
805
806        dataPtr = new Rappture::Unirect3d(nComponents);
807        if (!dataPtr->ImportDx(result, nComponents, length - 4, bytes + 4)) {
808            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
809            delete dataPtr;
810            return TCL_ERROR;
811        }
812        flowPtr->SetData(dataPtr);
813    } else if ((length > 11) && (strncmp(bytes, "<unirect3d>", 11) == 0)) {
814        Rappture::Unirect3d *dataPtr;
815        Tcl_CmdInfo cmdInfo;
816
817        /* Set the clientdata field of the unirect3d command to contain
818         * the local data structure. */
819        dataPtr = new Rappture::Unirect3d(nComponents);
820        if (!Tcl_GetCommandInfo(interp, "unirect3d", &cmdInfo)) {
821            return TCL_ERROR;
822        }
823        cmdInfo.objClientData = (ClientData)dataPtr;   
824        Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
825        if (Tcl_Eval(interp, bytes+11) != TCL_OK) {
826            delete dataPtr;
827            return TCL_ERROR;
828        }
829        if (!dataPtr->isInitialized()) {
830            delete dataPtr;
831            return TCL_ERROR;
832        }
833        flowPtr->SetData(dataPtr);
834    } else if ((length > 11) && (strncmp(bytes, "<unirect2d>", 11) == 0)) {
835        Rappture::Unirect2d *dataPtr;
836        Tcl_CmdInfo cmdInfo;
837
838        /* Set the clientdata field of the unirect3d command to contain
839         * the local data structure. */
840        dataPtr = new Rappture::Unirect2d();
841        if (!Tcl_GetCommandInfo(interp, "unirect2d", &cmdInfo)) {
842            return TCL_ERROR;
843        }
844        cmdInfo.objClientData = (ClientData)dataPtr;   
845        Tcl_SetCommandInfo(interp, "unirect2d", &cmdInfo);
846        if (Tcl_Eval(interp, bytes+11) != TCL_OK) {
847            delete dataPtr;
848            return TCL_ERROR;
849        }
850        if (!dataPtr->isInitialized()) {
851            delete dataPtr;
852            return TCL_ERROR;
853        }
854        Rappture::Unirect3d *d3Ptr = new Rappture::Unirect3d(nComponents);
855        d3Ptr->Convert(dataPtr);
856        flowPtr->SetData(d3Ptr);
857        delete dataPtr;
858    } else {
859        Rappture::Unirect3d *dataPtr;
860
861        dataPtr = new Rappture::Unirect3d(nComponents);
862        if (!dataPtr->ImportDx(result, nComponents, length, bytes)) {
863            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
864            delete dataPtr;
865            return TCL_ERROR;
866        }
867        flowPtr->SetData(dataPtr);
868    }
869    {
870        char info[1024];
871        ssize_t nWritten;
872        size_t length;
873
874        length = sprintf(info, "nv>data tag %s id 0 min %g max %g vmin %g vmax %g\n",
875                         flowPtr->name(), NanoVis::magMin,
876                         NanoVis::magMax, NanoVis::xMin, NanoVis::xMax);
877        nWritten  = write(0, info, length);
878        assert(nWritten == (ssize_t)strlen(info));
879    }
880    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
881    return TCL_OK;
882}
883
884static Rappture::CmdSpec flowDataOps[] = {
885    {"file",    2, FlowDataFileOp,    5, 5, "fileName nComponents",},
886    {"follows", 2, FlowDataFollowsOp, 5, 5, "size nComponents",},
887};
888static int nFlowDataOps = NumCmdSpecs(flowDataOps);
889
890static int
891FlowDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
892           Tcl_Obj *const *objv)
893{
894    Tcl_ObjCmdProc *proc;
895
896    proc = Rappture::GetOpFromObj(interp, nFlowDataOps, flowDataOps,
897                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
898    if (proc == NULL) {
899        return TCL_ERROR;
900    }
901    return (*proc) (clientData, interp, objc, objv);
902}
903
904float
905FlowCmd::GetRelativePosition(FlowPosition *posPtr)
906{
907    if (posPtr->flags == RELPOS) {
908        return posPtr->value;
909    }
910    switch (posPtr->axis) {
911    case AXIS_X: 
912        return (posPtr->value - NanoVis::xMin) /
913            (NanoVis::xMax - NanoVis::xMin);
914    case AXIS_Y: 
915        return (posPtr->value - NanoVis::yMin) /
916            (NanoVis::yMax - NanoVis::yMin);
917    case AXIS_Z: 
918        return (posPtr->value - NanoVis::zMin) /
919            (NanoVis::zMax - NanoVis::zMin);
920    }
921    return 0.0;
922}
923
924float
925FlowCmd::GetRelativePosition(void)
926{
927    return FlowCmd::GetRelativePosition(&_sv.slicePos);
928}
929
930/* Static NanoVis class commands. */
931
932void
933NanoVis::InitFlows(void)
934{
935    Tcl_InitHashTable(&flowTable, TCL_STRING_KEYS);
936}
937
938FlowCmd *
939NanoVis::FirstFlow(FlowIterator *iterPtr)
940{
941    iterPtr->hashPtr = Tcl_FirstHashEntry(&flowTable, &iterPtr->hashSearch);
942    if (iterPtr->hashPtr == NULL) {
943        return NULL;
944    }
945    return (FlowCmd *)Tcl_GetHashValue(iterPtr->hashPtr);
946}
947
948FlowCmd *
949NanoVis::NextFlow(FlowIterator *iterPtr)
950{
951    if (iterPtr->hashPtr == NULL) {
952        return NULL;
953    }
954    iterPtr->hashPtr = Tcl_NextHashEntry(&iterPtr->hashSearch);
955    if (iterPtr->hashPtr == NULL) {
956        return NULL;
957    }
958    return (FlowCmd *)Tcl_GetHashValue(iterPtr->hashPtr);
959}
960
961int
962NanoVis::GetFlow(Tcl_Interp *interp, Tcl_Obj *objPtr, FlowCmd **flowPtrPtr)
963{
964    Tcl_HashEntry *hPtr;
965    hPtr = Tcl_FindHashEntry(&flowTable, Tcl_GetString(objPtr));
966    if (hPtr == NULL) {
967        if (interp != NULL) {
968            Tcl_AppendResult(interp, "can't find a flow \"",
969                             Tcl_GetString(objPtr), "\"", (char *)NULL);
970        }
971        return TCL_ERROR;
972    }
973    *flowPtrPtr = (FlowCmd *)Tcl_GetHashValue(hPtr);
974    return TCL_OK;
975}
976
977int
978NanoVis::CreateFlow(Tcl_Interp *interp, Tcl_Obj *objPtr)
979{
980    Tcl_HashEntry *hPtr;
981    int isNew;
982    const char *name;
983    name = Tcl_GetString(objPtr);
984    hPtr = Tcl_CreateHashEntry(&flowTable, name, &isNew);
985    if (!isNew) {
986        Tcl_AppendResult(interp, "flow \"", name, "\" already exists.",
987                         (char *)NULL);
988        return TCL_ERROR;
989    }
990    Tcl_CmdInfo cmdInfo;
991    if (Tcl_GetCommandInfo(interp, name, &cmdInfo)) {
992        Tcl_AppendResult(interp, "an another command \"", name,
993                         "\" already exists.", (char *)NULL);
994        return TCL_ERROR;
995    }   
996    FlowCmd *flowPtr;
997    name = Tcl_GetHashKey(&flowTable, hPtr);
998    flowPtr = new FlowCmd(interp, name, hPtr);
999    if (flowPtr == NULL) {
1000        Tcl_AppendResult(interp, "can't allocate a flow object \"", name,
1001                         "\"", (char *)NULL);
1002        return TCL_ERROR;
1003    }
1004    Tcl_SetHashValue(hPtr, flowPtr);
1005    EventuallyRedraw(MAP_FLOWS);
1006    return TCL_OK;
1007}
1008
1009void
1010NanoVis::DeleteFlows(Tcl_Interp *interp)
1011{
1012    FlowCmd *flowPtr;
1013    FlowIterator iter;
1014    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1015         flowPtr = NextFlow(&iter)) {
1016        flowPtr->disconnect();          /* Don't disrupt the hash walk */
1017        Tcl_DeleteCommand(interp, flowPtr->name());
1018    }
1019    Tcl_DeleteHashTable(&flowTable);
1020}
1021
1022bool
1023NanoVis::MapFlows(void)
1024{
1025    flags &= ~MAP_FLOWS;
1026
1027
1028    /*
1029     * Step 1.  Get the overall min and max magnitudes of all the
1030     *          flow vectors.
1031     */
1032    FlowCmd *flowPtr;
1033    FlowIterator iter;
1034    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1035         flowPtr = NextFlow(&iter)) {
1036        double min, max;
1037        if (!flowPtr->isDataLoaded()) {
1038            continue;
1039        }
1040        Rappture::Unirect3d *dataPtr;
1041        dataPtr = flowPtr->GetData();
1042        min = dataPtr->magMin();
1043        max = dataPtr->magMax();
1044        if (min < magMin) {
1045            magMin = min;
1046        }
1047        if (max > magMax) {
1048            magMax = max;
1049        }
1050        if (dataPtr->xMin() < xMin) {
1051            xMin = dataPtr->xMin();
1052        }
1053        if (dataPtr->yMin() < yMin) {
1054            yMin = dataPtr->yMin();
1055        }
1056        if (dataPtr->zMin() < zMin) {
1057            zMin = dataPtr->zMin();
1058        }
1059        if (dataPtr->xMax() > xMax) {
1060            xMax = dataPtr->xMax();
1061        }
1062        if (dataPtr->yMax() > yMax) {
1063            yMax = dataPtr->yMax();
1064        }
1065        if (dataPtr->zMax() > zMax) {
1066            zMax = dataPtr->zMax();
1067        }
1068    }
1069
1070    /*
1071     * Step 2.  Generate the vector field from each data set.
1072     *          Delete the currently generated fields.
1073     */
1074    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1075         flowPtr = NextFlow(&iter)) {
1076        if (!flowPtr->isDataLoaded()) {
1077            continue;
1078        }
1079        flowPtr->InitVectorField();
1080        if (!flowPtr->visible()) {
1081            continue;
1082        }
1083        flowPtr->InitializeParticles();
1084        if (!flowPtr->ScaleVectorField()) {
1085            return false;
1086        }
1087        licRenderer->set_offset(flowPtr->GetRelativePosition());
1088    }
1089    AdvectFlows();
1090    return true;
1091}
1092
1093void
1094NanoVis::RenderFlows(void)
1095{
1096    FlowCmd *flowPtr;
1097    FlowIterator iter;
1098    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1099         flowPtr = NextFlow(&iter)) {
1100        if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
1101            flowPtr->Render();
1102        }
1103    }
1104    flags &= ~REDRAW_PENDING;
1105}
1106
1107void
1108NanoVis::ResetFlows(void)
1109{
1110    FlowCmd *flowPtr;
1111    FlowIterator iter;
1112    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1113         flowPtr = NextFlow(&iter)) {
1114        if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
1115            flowPtr->ResetParticles();
1116        }
1117    }
1118}   
1119
1120void
1121NanoVis::AdvectFlows(void)
1122{
1123    FlowCmd *flowPtr;
1124    FlowIterator iter;
1125    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1126         flowPtr = NextFlow(&iter)) {
1127        if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
1128            flowPtr->Advect();
1129        }
1130    }
1131}   
1132
1133/*
1134 *---------------------------------------------------------------------------
1135 *
1136 * AxisSwitchProc --
1137 *
1138 *      Convert a Tcl_Obj representing the label of a child node into its
1139 *      integer node id.
1140 *
1141 * Results:
1142 *      The return value is a standard Tcl result.
1143 *
1144 *---------------------------------------------------------------------------
1145 */
1146/*ARGSUSED*/
1147static int
1148AxisSwitchProc(
1149    ClientData clientData,      /* Flag indicating if the node is considered
1150                                 * before or after the insertion position. */
1151    Tcl_Interp *interp,         /* Interpreter to send results back to */
1152    const char *switchName,     /* Not used. */
1153    Tcl_Obj *objPtr,            /* String representation */
1154    char *record,               /* Structure record */
1155    int offset,                 /* Not used. */
1156    int flags)                  /* Not used. */
1157{
1158    const char *string = Tcl_GetString(objPtr);
1159    if (string[1] == '\0') {
1160        FlowCmd::SliceAxis *axisPtr = (FlowCmd::SliceAxis *)(record + offset);
1161        char c;
1162        c = tolower((unsigned char)string[0]);
1163        if (c == 'x') {
1164            *axisPtr = FlowCmd::AXIS_X;
1165            return TCL_OK;
1166        } else if (c == 'y') {
1167            *axisPtr = FlowCmd::AXIS_Y;
1168            return TCL_OK;
1169        } else if (c == 'z') {
1170            *axisPtr = FlowCmd::AXIS_Z;
1171            return TCL_OK;
1172        }
1173        /*FALLTHRU*/
1174    }
1175    Tcl_AppendResult(interp, "bad axis \"", string,
1176                     "\": should be x, y, or z", (char*)NULL);
1177    return TCL_ERROR;
1178}
1179
1180/*
1181 *---------------------------------------------------------------------------
1182 *
1183 * ColorSwitchProc --
1184 *
1185 *      Convert a Tcl_Obj representing the label of a list of four color
1186 *      components in to a RGBA color value.
1187 *
1188 * Results:
1189 *      The return value is a standard Tcl result.
1190 *
1191 *---------------------------------------------------------------------------
1192 */
1193/*ARGSUSED*/
1194static int
1195ColorSwitchProc(
1196    ClientData clientData,      /* Flag indicating if the node is considered
1197                                 * before or after the insertion position. */
1198    Tcl_Interp *interp,         /* Interpreter to send results back to */
1199    const char *switchName,     /* Not used. */
1200    Tcl_Obj *objPtr,            /* String representation */
1201    char *record,               /* Structure record */
1202    int offset,                 /* Not used. */
1203    int flags)                  /* Not used. */
1204{
1205    Tcl_Obj **objv;
1206    int objc;
1207    FlowColor *colorPtr = (FlowColor *)(record + offset);
1208
1209    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
1210        return TCL_ERROR;
1211    }
1212    if ((objc < 3) || (objc > 4)) {
1213        Tcl_AppendResult(interp, "wrong # of elements in color definition",
1214                         (char *)NULL);
1215        return TCL_ERROR;
1216    }
1217    float values[4];
1218    int i;
1219    values[3] = 1.0f;
1220    for (i = 0; i < objc; i++) {
1221        float value;
1222
1223        if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
1224            return TCL_ERROR;
1225        }
1226        if ((value < 0.0) || (value > 1.0)) {
1227            Tcl_AppendResult(interp, "bad component value in \"",
1228                Tcl_GetString(objPtr), "\": color values must be [0..1]",
1229                (char *)NULL);
1230            return TCL_ERROR;
1231        }
1232        values[i] = value;
1233    }       
1234    colorPtr->r = values[0];
1235    colorPtr->g = values[1];
1236    colorPtr->b = values[2];
1237    colorPtr->a = values[3];
1238    return TCL_OK;
1239}
1240
1241/*
1242 *---------------------------------------------------------------------------
1243 *
1244 * PointSwitchProc --
1245 *
1246 *      Convert a Tcl_Obj representing the a 3-D coordinate into
1247 *      a point.
1248 *
1249 * Results:
1250 *      The return value is a standard Tcl result.
1251 *
1252 *---------------------------------------------------------------------------
1253 */
1254/*ARGSUSED*/
1255static int
1256PointSwitchProc(
1257    ClientData clientData,      /* Flag indicating if the node is considered
1258                                 * before or after the insertion position. */
1259    Tcl_Interp *interp,         /* Interpreter to send results back to */
1260    const char *switchName,     /* Not used. */
1261    Tcl_Obj *objPtr,            /* String representation */
1262    char *record,               /* Structure record */
1263    int offset,                 /* Not used. */
1264    int flags)                  /* Not used. */
1265{
1266    FlowPoint *pointPtr = (FlowPoint *)(record + offset);
1267    int objc;
1268    Tcl_Obj **objv;
1269
1270    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
1271        return TCL_ERROR;
1272    }
1273    if (objc != 3) {
1274        Tcl_AppendResult(interp, "wrong # of elements for box coordinates: "
1275                         " should be \"x y z\"", (char *)NULL);
1276        return TCL_ERROR;
1277    }
1278    float values[3];
1279    int i;
1280    for (i = 0; i < objc; i++) {
1281        float value;
1282
1283        if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
1284            return TCL_ERROR;
1285        }
1286        values[i] = value;
1287    }       
1288    pointPtr->x = values[0];
1289    pointPtr->y = values[1];
1290    pointPtr->z = values[2];
1291    return TCL_OK;
1292}
1293
1294/*
1295 *---------------------------------------------------------------------------
1296 *
1297 * PositionSwitchProc --
1298 *
1299 *      Convert a Tcl_Obj representing the a 3-D coordinate into
1300 *      a point.
1301 *
1302 * Results:
1303 *      The return value is a standard Tcl result.
1304 *
1305 *---------------------------------------------------------------------------
1306 */
1307/*ARGSUSED*/
1308static int
1309PositionSwitchProc(
1310    ClientData clientData,      /* Flag indicating if the node is considered
1311                                 * before or after the insertion position. */
1312    Tcl_Interp *interp,         /* Interpreter to send results back to */
1313    const char *switchName,     /* Not used. */
1314    Tcl_Obj *objPtr,            /* String representation */
1315    char *record,               /* Structure record */
1316    int offset,                 /* Not used. */
1317    int flags)                  /* Not used. */
1318{
1319    FlowPosition *posPtr = (FlowPosition *)(record + offset);
1320    const char *string;
1321    char *p;
1322
1323    string = Tcl_GetString(objPtr);
1324    p = strrchr(string, '%');
1325    if (p == NULL) {
1326        float value;
1327
1328        if (GetFloatFromObj(interp, objPtr, &value) != TCL_OK) {
1329            return TCL_ERROR;
1330        }
1331        posPtr->value = value;
1332        posPtr->flags = ABSPOS;
1333    } else {
1334        double value;
1335
1336        *p = '\0';
1337        if (Tcl_GetDouble(interp, string, &value) != TCL_OK) {
1338            return TCL_ERROR;
1339        }
1340        posPtr->value = (float)value * 0.01;
1341        posPtr->flags = RELPOS;
1342    }
1343    return TCL_OK;
1344}
1345
1346/*
1347 *---------------------------------------------------------------------------
1348 *
1349 * TransferFunctionSwitchProc --
1350 *
1351 *      Convert a Tcl_Obj representing the transfer function into a
1352 *      TransferFunction pointer.  The transfer function must have been
1353 *      previously defined.
1354 *
1355 * Results:
1356 *      The return value is a standard Tcl result.
1357 *
1358 *---------------------------------------------------------------------------
1359 */
1360/*ARGSUSED*/
1361static int
1362TransferFunctionSwitchProc(
1363    ClientData clientData,      /* Flag indicating if the node is considered
1364                                 * before or after the insertion position. */
1365    Tcl_Interp *interp,         /* Interpreter to send results back to */
1366    const char *switchName,     /* Not used. */
1367    Tcl_Obj *objPtr,            /* String representation */
1368    char *record,               /* Structure record */
1369    int offset,                 /* Not used. */
1370    int flags)                  /* Not used. */
1371{
1372    TransferFunction **funcPtrPtr = (TransferFunction **)(record + offset);
1373    TransferFunction *funcPtr;
1374    funcPtr = NanoVis::get_transfunc(Tcl_GetString(objPtr));
1375    if (funcPtr == NULL) {
1376        Tcl_AppendResult(interp, "transfer function \"", Tcl_GetString(objPtr),
1377                         "\" is not defined", (char*)NULL);
1378        return TCL_ERROR;
1379    }
1380    *funcPtrPtr = funcPtr;
1381    return TCL_OK;
1382}
1383
1384static int
1385FlowConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1386                Tcl_Obj *const *objv)
1387{
1388    FlowCmd *flowPtr = (FlowCmd *)clientData;
1389
1390    if (flowPtr->ParseSwitches(interp, objc - 2, objv + 2) != TCL_OK) {
1391        return TCL_ERROR;
1392    }
1393    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1394    return TCL_OK;
1395}
1396
1397static int
1398FlowParticlesAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1399                   Tcl_Obj *const *objv)
1400{
1401    FlowCmd *flowPtr = (FlowCmd *)clientData;
1402
1403    if (flowPtr->CreateParticles(interp, objv[3]) != TCL_OK) {
1404        return TCL_ERROR;
1405    }
1406    FlowParticles *particlesPtr;
1407    if (flowPtr->GetParticles(interp, objv[3], &particlesPtr) != TCL_OK) {
1408        return TCL_ERROR;
1409    }
1410    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1411        delete particlesPtr;
1412        return TCL_ERROR;
1413    }
1414    particlesPtr->Configure();
1415    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1416    Tcl_SetObjResult(interp, objv[3]);
1417    return TCL_OK;
1418}
1419
1420static int
1421FlowParticlesConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1422                         Tcl_Obj *const *objv)
1423{
1424    FlowCmd *flowPtr = (FlowCmd *)clientData;
1425
1426    FlowParticles *particlesPtr;
1427    if (flowPtr->GetParticles(interp, objv[3], &particlesPtr) != TCL_OK) {
1428        return TCL_ERROR;
1429    }
1430    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1431        return TCL_ERROR;
1432    }
1433    particlesPtr->Configure();
1434    NanoVis::EventuallyRedraw();
1435    return TCL_OK;
1436}
1437
1438static int
1439FlowParticlesDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1440                      Tcl_Obj *const *objv)
1441{
1442    FlowCmd *flowPtr = (FlowCmd *)clientData;
1443    int i;
1444    for (i = 3; i < objc; i++) {
1445        FlowParticles *particlesPtr;
1446
1447        if (flowPtr->GetParticles(NULL, objv[i], &particlesPtr) == TCL_OK) {
1448            delete particlesPtr;
1449        }
1450    }
1451    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1452    return TCL_OK;
1453}
1454
1455static int
1456FlowParticlesNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1457                     Tcl_Obj *const *objv)
1458{
1459    FlowCmd *flowPtr = (FlowCmd *)clientData;
1460    Tcl_Obj *listObjPtr;
1461    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1462    FlowParticlesIterator iter;
1463    FlowParticles *particlesPtr;
1464    for (particlesPtr = flowPtr->FirstParticles(&iter); particlesPtr != NULL;
1465         particlesPtr = flowPtr->NextParticles(&iter)) {
1466        Tcl_Obj *objPtr;
1467
1468        objPtr = Tcl_NewStringObj(particlesPtr->name(), -1);
1469        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1470    }
1471    Tcl_SetObjResult(interp, listObjPtr);
1472    return TCL_OK;
1473}
1474
1475/*
1476 *---------------------------------------------------------------------------
1477 *
1478 * FlowParticlesObjCmd --
1479 *
1480 *      This procedure is invoked to process commands on behalf of the flow
1481 *      object.
1482 *
1483 * Results:
1484 *      A standard Tcl result.
1485 *
1486 * Side effects:
1487 *      See the user documentation.
1488 *
1489 * $flow particles oper $name
1490 *---------------------------------------------------------------------------
1491 */
1492static Rappture::CmdSpec flowParticlesOps[] = {
1493    {"add",        1, FlowParticlesAddOp,        4, 0, "name ?switches?",},
1494    {"configure",  1, FlowParticlesConfigureOp,  4, 0, "name ?switches?",},
1495    {"delete",     1, FlowParticlesDeleteOp,     4, 0, "?name...?"},
1496    {"names",      1, FlowParticlesNamesOp,      3, 4, "?pattern?"},
1497};
1498
1499static int nFlowParticlesOps = NumCmdSpecs(flowParticlesOps);
1500
1501static int
1502FlowParticlesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1503               Tcl_Obj *const *objv)
1504{
1505    Tcl_ObjCmdProc *proc;
1506    proc = Rappture::GetOpFromObj(interp, nFlowParticlesOps, flowParticlesOps,
1507        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1508    if (proc == NULL) {
1509        return TCL_ERROR;
1510    }
1511    FlowCmd *flowPtr = (FlowCmd *)clientData;
1512    Tcl_Preserve(flowPtr);
1513    int result;
1514    result = (*proc) (clientData, interp, objc, objv);
1515    Tcl_Release(flowPtr);
1516    return result;
1517}
1518
1519static int
1520FlowBoxAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1521               Tcl_Obj *const *objv)
1522{
1523    FlowCmd *flowPtr = (FlowCmd *)clientData;
1524
1525    if (flowPtr->CreateBox(interp, objv[3]) != TCL_OK) {
1526        return TCL_ERROR;
1527    }
1528    FlowBox *boxPtr;
1529    if (flowPtr->GetBox(interp, objv[3], &boxPtr) != TCL_OK) {
1530        return TCL_ERROR;
1531    }
1532    if (boxPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1533        delete boxPtr;
1534        return TCL_ERROR;
1535    }
1536    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1537    Tcl_SetObjResult(interp, objv[3]);
1538    return TCL_OK;
1539}
1540
1541static int
1542FlowBoxDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1543                   Tcl_Obj *const *objv)
1544{
1545    FlowCmd *flowPtr = (FlowCmd *)clientData;
1546    int i;
1547    for (i = 3; i < objc; i++) {
1548        FlowBox *boxPtr;
1549
1550        if (flowPtr->GetBox(NULL, objv[i], &boxPtr) == TCL_OK) {
1551            delete boxPtr;
1552        }
1553    }
1554    NanoVis::EventuallyRedraw();
1555    return TCL_OK;
1556}
1557
1558static int
1559FlowBoxNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1560             Tcl_Obj *const *objv)
1561{
1562    FlowCmd *flowPtr = (FlowCmd *)clientData;
1563    Tcl_Obj *listObjPtr;
1564    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1565    FlowBoxIterator iter;
1566    FlowBox *boxPtr;
1567    for (boxPtr = flowPtr->FirstBox(&iter); boxPtr != NULL;
1568         boxPtr = flowPtr->NextBox(&iter)) {
1569        Tcl_Obj *objPtr;
1570
1571        objPtr = Tcl_NewStringObj(boxPtr->name(), -1);
1572        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1573    }
1574    Tcl_SetObjResult(interp, listObjPtr);
1575    return TCL_OK;
1576}
1577
1578static int
1579FlowBoxConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1580                        Tcl_Obj *const *objv)
1581{
1582    FlowCmd *flowPtr = (FlowCmd *)clientData;
1583
1584    FlowBox *boxPtr;
1585    if (flowPtr->GetBox(interp, objv[3], &boxPtr) != TCL_OK) {
1586        return TCL_ERROR;
1587    }
1588    if (boxPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1589        return TCL_ERROR;
1590    }
1591    NanoVis::EventuallyRedraw();
1592    return TCL_OK;
1593}
1594
1595/*
1596 *---------------------------------------------------------------------------
1597 *
1598 * FlowBoxOp--
1599 *
1600 *      This procedure is invoked to process commands on behalf of the flow
1601 *      object.
1602 *
1603 * Results:
1604 *      A standard Tcl result.
1605 *
1606 * Side effects:
1607 *      See the user documentation.
1608 *
1609 *---------------------------------------------------------------------------
1610 */
1611static Rappture::CmdSpec flowBoxOps[] = {
1612    {"add",        1, FlowBoxAddOp,        4, 0, "name ?switches?",},
1613    {"configure",  1, FlowBoxConfigureOp,  4, 0, "name ?switches?",},
1614    {"delete",     1, FlowBoxDeleteOp,     3, 0, "?name...?"},
1615    {"names",      1, FlowBoxNamesOp,      3, 0, "?pattern?"},
1616};
1617
1618static int nFlowBoxOps = NumCmdSpecs(flowBoxOps);
1619
1620static int
1621FlowBoxOp(ClientData clientData, Tcl_Interp *interp, int objc,
1622               Tcl_Obj *const *objv)
1623{
1624    Tcl_ObjCmdProc *proc;
1625    proc = Rappture::GetOpFromObj(interp, nFlowBoxOps, flowBoxOps,
1626        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1627    if (proc == NULL) {
1628        return TCL_ERROR;
1629    }
1630    FlowCmd *flowPtr = (FlowCmd *)clientData;
1631    Tcl_Preserve(flowPtr);
1632    int result;
1633    result = (*proc) (clientData, interp, objc, objv);
1634    Tcl_Release(flowPtr);
1635    return result;
1636}
1637
1638/*
1639 *---------------------------------------------------------------------------
1640 *
1641 * FlowInstObjCmd --
1642 *
1643 *      This procedure is invoked to process commands on behalf of the flow
1644 *      object.
1645 *
1646 * Results:
1647 *      A standard Tcl result.
1648 *
1649 * Side effects:
1650 *      See the user documentation.
1651 *
1652 *---------------------------------------------------------------------------
1653 */
1654static Rappture::CmdSpec flowInstOps[] = {
1655    {"box",         1, FlowBoxOp,        2, 0, "oper ?args?"},
1656    {"configure",   1, FlowConfigureOp,  2, 0, "?switches?"},
1657    {"data",        1, FlowDataOp,       2, 0, "oper ?args?"},
1658    {"particles",   1, FlowParticlesOp,  2, 0, "oper ?args?"}
1659};
1660static int nFlowInstOps = NumCmdSpecs(flowInstOps);
1661
1662static int
1663FlowInstObjCmd(ClientData clientData, Tcl_Interp *interp, int objc,
1664               Tcl_Obj *const *objv)
1665{
1666    Tcl_ObjCmdProc *proc;
1667    proc = Rappture::GetOpFromObj(interp, nFlowInstOps, flowInstOps,
1668        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1669    if (proc == NULL) {
1670        return TCL_ERROR;
1671    }
1672    assert(CheckGL(AT));
1673    FlowCmd *flowPtr = (FlowCmd *)clientData;
1674    Tcl_Preserve(flowPtr);
1675    int result;
1676    result = (*proc) (clientData, interp, objc, objv);
1677    Tcl_Release(flowPtr);
1678    return result;
1679}
1680
1681/*
1682 *---------------------------------------------------------------------------
1683 *
1684 * FlowInstDeleteProc --
1685 *
1686 *      Deletes the command associated with the tree.  This is called only
1687 *      when the command associated with the tree is destroyed.
1688 *
1689 * Results:
1690 *      None.
1691 *
1692 *---------------------------------------------------------------------------
1693 */
1694static void
1695FlowInstDeleteProc(ClientData clientData)
1696{
1697    FlowCmd *flowPtr = (FlowCmd *)clientData;
1698    delete flowPtr;
1699}
1700
1701/*
1702 *---------------------------------------------------------------------------
1703 *
1704 * FlowAddOp --
1705 *
1706 *---------------------------------------------------------------------------
1707 */
1708/*ARGSUSED*/
1709static int
1710FlowAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1711          Tcl_Obj *const *objv)
1712{
1713    if (NanoVis::CreateFlow(interp, objv[2]) != TCL_OK) {
1714        return TCL_ERROR;
1715    }
1716    FlowCmd *flowPtr;
1717    if (NanoVis::GetFlow(interp, objv[2], &flowPtr) != TCL_OK) {
1718        return TCL_ERROR;
1719    }
1720    if (flowPtr->ParseSwitches(interp, objc - 3, objv + 3) != TCL_OK) {
1721        Tcl_DeleteCommand(interp, flowPtr->name());
1722        return TCL_ERROR;
1723    }
1724    Tcl_SetObjResult(interp, objv[2]);
1725    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1726    return TCL_OK;
1727}
1728
1729/*
1730 *---------------------------------------------------------------------------
1731 *
1732 * FlowDeleteOp --
1733 *
1734 *---------------------------------------------------------------------------
1735 */
1736/*ARGSUSED*/
1737static int
1738FlowDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1739             Tcl_Obj *const *objv)
1740{
1741    int i;
1742
1743    for (i = 2; i < objc; i++) {
1744        FlowCmd *flowPtr;
1745
1746        if (NanoVis::GetFlow(interp, objv[i], &flowPtr) != TCL_OK) {
1747            return TCL_ERROR;
1748        }
1749        Tcl_DeleteCommand(interp, flowPtr->name());
1750    }
1751    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1752    return TCL_OK;
1753}
1754
1755/*
1756 *---------------------------------------------------------------------------
1757 *
1758 * FlowExistsOp --
1759 *
1760 *---------------------------------------------------------------------------
1761 */
1762/*ARGSUSED*/
1763static int
1764FlowExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1765             Tcl_Obj *const *objv)
1766{
1767    bool value;
1768    FlowCmd *flowPtr;
1769
1770    value = false;
1771    if (NanoVis::GetFlow(NULL, objv[2], &flowPtr) == TCL_OK) {
1772        value = true;
1773    }
1774    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
1775    return TCL_OK;
1776}
1777
1778/*
1779 *---------------------------------------------------------------------------
1780 *
1781 * FlowNamesOp --
1782 *
1783 *---------------------------------------------------------------------------
1784 */
1785/*ARGSUSED*/
1786static int
1787FlowNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1788            Tcl_Obj *const *objv)
1789{
1790    Tcl_Obj *listObjPtr;
1791    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1792    FlowCmd *flowPtr;
1793    FlowIterator iter;
1794    for (flowPtr = NanoVis::FirstFlow(&iter); flowPtr != NULL;
1795         flowPtr = NanoVis::NextFlow(&iter)) {
1796        Tcl_Obj *objPtr;
1797
1798        objPtr = Tcl_NewStringObj(flowPtr->name(), -1);
1799        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1800    }
1801    Tcl_SetObjResult(interp, listObjPtr);
1802    return TCL_OK;
1803}
1804
1805static int
1806FlowNextOp(ClientData clientData, Tcl_Interp *interp, int objc,
1807             Tcl_Obj *const *objv)
1808{
1809    assert(NanoVis::licRenderer != NULL);
1810    if (NanoVis::flags & NanoVis::MAP_FLOWS) {
1811        NanoVis::MapFlows();
1812    }
1813    NanoVis::EventuallyRedraw();
1814    NanoVis::licRenderer->convolve();
1815    NanoVis::AdvectFlows();
1816    return TCL_OK;
1817}
1818
1819static int
1820FlowResetOp(ClientData clientData, Tcl_Interp *interp, int objc,
1821             Tcl_Obj *const *objv)
1822{
1823    NanoVis::ResetFlows();
1824    NanoVis::licRenderer->reset();
1825    return TCL_OK;
1826}
1827
1828static int
1829FlowVideoOp(ClientData clientData, Tcl_Interp *interp, int objc,
1830            Tcl_Obj *const *objv)
1831{
1832    int width, height;          // Resolution of video.
1833    int numFrames;              // Total number of frames.
1834    float frameRate;            // Frame rate of the video.
1835    float bitRate;              // Bit rate of the vide.
1836
1837    if ((Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK) ||
1838        (Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) ||
1839        (Tcl_GetIntFromObj(interp, objv[4], &numFrames) != TCL_OK) ||
1840        (GetFloatFromObj(interp, objv[5], &frameRate) != TCL_OK) ||
1841        (GetFloatFromObj(interp, objv[6], &bitRate) != TCL_OK)) {
1842        return TCL_ERROR;
1843    }
1844    if ((width<0) || (width>SHRT_MAX) || (height<0) || (height>SHRT_MAX)) {
1845        Tcl_AppendResult(interp, "bad dimensions for video", (char *)NULL);
1846        return TCL_ERROR;
1847    }
1848    if ((frameRate < 0.0f) || (frameRate > 30.0f)) {
1849        Tcl_AppendResult(interp, "bad frame rate \"", Tcl_GetString(objv[5]),
1850                         "\"", (char *)NULL);
1851        return TCL_ERROR;
1852    }
1853    if ((bitRate < 0.0f) || (frameRate > 30.0f)) {
1854        Tcl_AppendResult(interp, "bad bit rate \"", Tcl_GetString(objv[6]),
1855                         "\"", (char *)NULL);
1856        return TCL_ERROR;
1857    }
1858    if (NanoVis::licRenderer == NULL) {
1859        Tcl_AppendResult(interp, "no lic renderer.", (char *)NULL);
1860        return TCL_ERROR;
1861    }
1862    if (NanoVis::flowVisRenderer == NULL) {
1863        Tcl_AppendResult(interp, "no flow renderer.", (char *)NULL);
1864        return TCL_ERROR;
1865    }
1866    // Save the old dimensions of the offscreen buffer.
1867    int oldWidth, oldHeight;
1868    oldWidth = NanoVis::win_width;
1869    oldHeight = NanoVis::win_height;
1870
1871    if ((width != oldWidth) || (height != oldHeight)) {
1872        // Resize to the requested size.
1873        NanoVis::resize_offscreen_buffer(width, height);
1874    }
1875
1876    char fileName[128];
1877    sprintf(fileName,"/tmp/flow%d.mpeg", getpid());
1878
1879    Trace("FLOW started\n");
1880
1881    Rappture::Outcome result;
1882    Rappture::AVTranslate movie(width, height, frameRate, bitRate);
1883
1884    int pad = 0;
1885    if ((3*NanoVis::win_width) % 4 > 0) {
1886        pad = 4 - ((3*NanoVis::win_width) % 4);
1887    }
1888
1889    movie.init(result, fileName);
1890
1891    for (int i = 0; i < numFrames; i++) {
1892        // Generate the latest frame and send it back to the client
1893        NanoVis::licRenderer->convolve();
1894        NanoVis::AdvectFlows();
1895        NanoVis::RenderFlows();
1896        NanoVis::offscreen_buffer_capture();  //enable offscreen render
1897        NanoVis::display();
1898
1899        NanoVis::read_screen();
1900        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1901
1902        // This is done before bmp_write_to_file because bmp_write_to_file
1903        // turns rgb data to bgr
1904        movie.append(result, NanoVis::screen_buffer, pad);
1905        // NanoVis::bmp_write_to_file(frame_count, fileName);
1906    }
1907
1908    movie.done(result);
1909    Trace("FLOW end\n");
1910
1911#ifdef notdef
1912    if (NanoVis::licRenderer) {
1913        NanoVis::licRenderer->active(false);
1914    }
1915#endif
1916    NanoVis::licRenderer->make_patterns();
1917
1918    // FIXME: find a way to get the data from the movie object as a void*
1919    Rappture::Buffer data;
1920    if (!data.load(result, fileName)) {
1921        Tcl_AppendResult(interp, "can't load data from temporary movie file \"",
1922                fileName, "\": ", result.remark(), (char *)NULL);
1923        return TCL_ERROR;
1924    }
1925    // Build the command string for the client.
1926    char command[200];
1927    sprintf(command,"nv>image -bytes %lu -type movie -token token\n",
1928            (unsigned long)data.size());
1929
1930    NanoVis::sendDataToClient(command, data.bytes(), data.size());
1931    if (unlink(fileName) != 0) {
1932        Tcl_AppendResult(interp, "can't unlink temporary movie file \"",
1933                fileName, "\": ", Tcl_PosixError(interp), (char *)NULL);
1934        return TCL_ERROR;
1935    }
1936    return TCL_OK;
1937}
1938
1939/*
1940 *---------------------------------------------------------------------------
1941 *
1942 * FlowObjCmd --
1943 *
1944 *---------------------------------------------------------------------------
1945 */
1946static Rappture::CmdSpec flowCmdOps[] = {
1947    {"add",      1, FlowAddOp,     3, 0, "name ?option value...?",},
1948    {"delete",   1, FlowDeleteOp,  2, 0, "name...",},
1949    {"exists",   1, FlowExistsOp,  3, 3, "name",},
1950    {"names",    1, FlowNamesOp,   2, 3, "?pattern?",},
1951    {"next",     2, FlowNextOp,    2, 2, "",},
1952    {"reset",    1, FlowResetOp,   2, 2, "",},
1953    {"video",    1, FlowVideoOp,   7, 7,       
1954        "width height numFrames frameRate bitRate ",},
1955};
1956static int nFlowCmdOps = NumCmdSpecs(flowCmdOps);
1957
1958/*ARGSUSED*/
1959static int
1960FlowCmdProc(ClientData clientData, Tcl_Interp *interp, int objc,
1961            Tcl_Obj *const *objv)
1962{
1963    Tcl_ObjCmdProc *proc;
1964
1965    proc = Rappture::GetOpFromObj(interp, nFlowCmdOps, flowCmdOps,
1966        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1967    if (proc == NULL) {
1968        return TCL_ERROR;
1969    }
1970    return (*proc) (clientData, interp, objc, objv);
1971}
1972
1973/*
1974 *---------------------------------------------------------------------------
1975 *
1976 * FlowCmdInitProc --
1977 *
1978 *      This procedure is invoked to initialize the "tree" command.
1979 *
1980 * Results:
1981 *      None.
1982 *
1983 * Side effects:
1984 *      Creates the new command and adds a new entry into a global Tcl
1985 *      associative array.
1986 *
1987 *---------------------------------------------------------------------------
1988 */
1989int
1990FlowCmdInitProc(Tcl_Interp *interp)
1991{
1992    Tcl_CreateObjCommand(interp, "flow", FlowCmdProc, NULL, NULL);
1993    NanoVis::InitFlows();
1994    return TCL_OK;
1995}
1996
Note: See TracBrowser for help on using the repository browser.