source: branches/blt4/packages/vizservers/nanovis/FlowCmd.cpp @ 2966

Last change on this file since 2966 was 2966, checked in by gah, 13 years ago

sync back with trunk

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