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

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

Add emacs mode magic line in preparation for indentation cleanup

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