source: branches/nanovis2/packages/vizservers/nanovis/FlowCmd.cpp @ 3351

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

Cleanups, add (strict) equality operators to vector classes, add matrix uniform
setters for NvShader? (that don't use GL state), remove old camera define/code,
add method to set clipping range of camera based on bounds (not used yet),
remove camera aim command (use orient instead), add camera position command,
add cleanup to destroy objects in NanoVis::removeAllData(), don't precede image
command to client with newline.

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