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

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