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

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

Make flow related methods use camel case convention

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