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

Last change on this file since 1478 was 1478, checked in by gah, 15 years ago

Fix volume management routines to handle deletion

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