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

Last change on this file since 2875 was 2875, checked in by ldelgass, 12 years ago

Don't use memset on this pointer -- will blow up if virtual methods are added.
Also formatting cleanups.

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