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

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

Some minor refactoring, also add some more fine grained config.h defines
(e.g. replace NV40 define with feature defines). Add tests for some required
OpenGL extensions (should always check for extensions or base version before
calling entry points from the extension). Also, clamp diffuse and specular
values on input and warn when they are out of range.

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