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

Last change on this file since 3502 was 3502, checked in by ldelgass, 6 years ago

Add basic VTK structured points reader to nanovis, update copyright dates.

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