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

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

update from branch

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