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

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

Flip image along y-axis for movie

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