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

Last change on this file since 3464 was 3452, checked in by ldelgass, 7 years ago

Remove XINETD define from nanovis. We only support server mode now, no glut
interaction loop with mouse/keyboard handlers. Fixes for trace logging to make
output closer to vtkvis: inlcude function name for trace messages, remove
newlines from format strings in macros since newlines get added by syslog.

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