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

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

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

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