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

Last change on this file since 1489 was 1484, checked in by vrinside, 15 years ago

added 2d arrows (arrow tip will be added)

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