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

Last change on this file since 1522 was 1522, checked in by gah, 15 years ago
File size: 58.3 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
1083    if (licRenderer->active()) {
1084        NanoVis::licRenderer->reset();
1085    }
1086    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1087         flowPtr = NextFlow(&iter)) {
1088        if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
1089            flowPtr->ResetParticles();
1090        }
1091    }
1092}   
1093
1094void
1095NanoVis::AdvectFlows(void)
1096{
1097    FlowCmd *flowPtr;
1098    FlowIterator iter;
1099    for (flowPtr = FirstFlow(&iter); flowPtr != NULL;
1100         flowPtr = NextFlow(&iter)) {
1101        if ((flowPtr->isDataLoaded()) && (flowPtr->visible())) {
1102            flowPtr->Advect();
1103        }
1104    }
1105}   
1106
1107/*
1108 *---------------------------------------------------------------------------
1109 *
1110 * AxisSwitchProc --
1111 *
1112 *      Convert a Tcl_Obj representing the label of a child node into its
1113 *      integer node id.
1114 *
1115 * Results:
1116 *      The return value is a standard Tcl result.
1117 *
1118 *---------------------------------------------------------------------------
1119 */
1120/*ARGSUSED*/
1121static int
1122AxisSwitchProc(
1123    ClientData clientData,      /* Flag indicating if the node is considered
1124                                 * before or after the insertion position. */
1125    Tcl_Interp *interp,         /* Interpreter to send results back to */
1126    const char *switchName,     /* Not used. */
1127    Tcl_Obj *objPtr,            /* String representation */
1128    char *record,               /* Structure record */
1129    int offset,                 /* Not used. */
1130    int flags)                  /* Not used. */
1131{
1132    const char *string = Tcl_GetString(objPtr);
1133    if (string[1] == '\0') {
1134        FlowCmd::SliceAxis *axisPtr = (FlowCmd::SliceAxis *)(record + offset);
1135        char c;
1136        c = tolower((unsigned char)string[0]);
1137        if (c == 'x') {
1138            *axisPtr = FlowCmd::AXIS_X;
1139            return TCL_OK;
1140        } else if (c == 'y') {
1141            *axisPtr = FlowCmd::AXIS_Y;
1142            return TCL_OK;
1143        } else if (c == 'z') {
1144            *axisPtr = FlowCmd::AXIS_Z;
1145            return TCL_OK;
1146        }
1147        /*FALLTHRU*/
1148    }
1149    Tcl_AppendResult(interp, "bad axis \"", string,
1150                     "\": should be x, y, or z", (char*)NULL);
1151    return TCL_ERROR;
1152}
1153
1154/*
1155 *---------------------------------------------------------------------------
1156 *
1157 * ColorSwitchProc --
1158 *
1159 *      Convert a Tcl_Obj representing the label of a list of four color
1160 *      components in to a RGBA color value.
1161 *
1162 * Results:
1163 *      The return value is a standard Tcl result.
1164 *
1165 *---------------------------------------------------------------------------
1166 */
1167/*ARGSUSED*/
1168static int
1169ColorSwitchProc(
1170    ClientData clientData,      /* Flag indicating if the node is considered
1171                                 * before or after the insertion position. */
1172    Tcl_Interp *interp,         /* Interpreter to send results back to */
1173    const char *switchName,     /* Not used. */
1174    Tcl_Obj *objPtr,            /* String representation */
1175    char *record,               /* Structure record */
1176    int offset,                 /* Not used. */
1177    int flags)                  /* Not used. */
1178{
1179    Tcl_Obj **objv;
1180    int objc;
1181    FlowColor *colorPtr = (FlowColor *)(record + offset);
1182
1183    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
1184        return TCL_ERROR;
1185    }
1186    if ((objc < 3) || (objc > 4)) {
1187        Tcl_AppendResult(interp, "wrong # of elements in color definition",
1188                         (char *)NULL);
1189        return TCL_ERROR;
1190    }
1191    float values[4];
1192    int i;
1193    values[3] = 1.0f;
1194    for (i = 0; i < objc; i++) {
1195        float value;
1196
1197        if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
1198            return TCL_ERROR;
1199        }
1200        if ((value < 0.0) || (value > 1.0)) {
1201            Tcl_AppendResult(interp, "bad component value in \"",
1202                Tcl_GetString(objPtr), "\": color values must be [0..1]",
1203                (char *)NULL);
1204            return TCL_ERROR;
1205        }
1206        values[i] = value;
1207    }       
1208    colorPtr->r = values[0];
1209    colorPtr->g = values[1];
1210    colorPtr->b = values[2];
1211    colorPtr->a = values[3];
1212    return TCL_OK;
1213}
1214
1215/*
1216 *---------------------------------------------------------------------------
1217 *
1218 * PointSwitchProc --
1219 *
1220 *      Convert a Tcl_Obj representing the a 3-D coordinate into
1221 *      a point.
1222 *
1223 * Results:
1224 *      The return value is a standard Tcl result.
1225 *
1226 *---------------------------------------------------------------------------
1227 */
1228/*ARGSUSED*/
1229static int
1230PointSwitchProc(
1231    ClientData clientData,      /* Flag indicating if the node is considered
1232                                 * before or after the insertion position. */
1233    Tcl_Interp *interp,         /* Interpreter to send results back to */
1234    const char *switchName,     /* Not used. */
1235    Tcl_Obj *objPtr,            /* String representation */
1236    char *record,               /* Structure record */
1237    int offset,                 /* Not used. */
1238    int flags)                  /* Not used. */
1239{
1240    FlowPoint *pointPtr = (FlowPoint *)(record + offset);
1241    int objc;
1242    Tcl_Obj **objv;
1243
1244    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
1245        return TCL_ERROR;
1246    }
1247    if (objc != 3) {
1248        Tcl_AppendResult(interp, "wrong # of elements for box coordinates: "
1249                         " should be \"x y z\"", (char *)NULL);
1250        return TCL_ERROR;
1251    }
1252    float values[3];
1253    int i;
1254    for (i = 0; i < objc; i++) {
1255        float value;
1256
1257        if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
1258            return TCL_ERROR;
1259        }
1260        values[i] = value;
1261    }       
1262    pointPtr->x = values[0];
1263    pointPtr->y = values[1];
1264    pointPtr->z = values[2];
1265    return TCL_OK;
1266}
1267
1268/*
1269 *---------------------------------------------------------------------------
1270 *
1271 * PositionSwitchProc --
1272 *
1273 *      Convert a Tcl_Obj representing the a 3-D coordinate into
1274 *      a point.
1275 *
1276 * Results:
1277 *      The return value is a standard Tcl result.
1278 *
1279 *---------------------------------------------------------------------------
1280 */
1281/*ARGSUSED*/
1282static int
1283PositionSwitchProc(
1284    ClientData clientData,      /* Flag indicating if the node is considered
1285                                 * before or after the insertion position. */
1286    Tcl_Interp *interp,         /* Interpreter to send results back to */
1287    const char *switchName,     /* Not used. */
1288    Tcl_Obj *objPtr,            /* String representation */
1289    char *record,               /* Structure record */
1290    int offset,                 /* Not used. */
1291    int flags)                  /* Not used. */
1292{
1293    FlowPosition *posPtr = (FlowPosition *)(record + offset);
1294    const char *string;
1295    char *p;
1296
1297    string = Tcl_GetString(objPtr);
1298    p = strrchr(string, '%');
1299    if (p == NULL) {
1300        float value;
1301
1302        if (GetFloatFromObj(interp, objPtr, &value) != TCL_OK) {
1303            return TCL_ERROR;
1304        }
1305        posPtr->value = value;
1306        posPtr->flags = ABSPOS;
1307    } else {
1308        double value;
1309
1310        *p = '\0';
1311        if (Tcl_GetDouble(interp, string, &value) != TCL_OK) {
1312            return TCL_ERROR;
1313        }
1314        posPtr->value = (float)value * 0.01;
1315        posPtr->flags = RELPOS;
1316    }
1317    return TCL_OK;
1318}
1319
1320/*
1321 *---------------------------------------------------------------------------
1322 *
1323 * TransferFunctionSwitchProc --
1324 *
1325 *      Convert a Tcl_Obj representing the transfer function into a
1326 *      TransferFunction pointer.  The transfer function must have been
1327 *      previously defined.
1328 *
1329 * Results:
1330 *      The return value is a standard Tcl result.
1331 *
1332 *---------------------------------------------------------------------------
1333 */
1334/*ARGSUSED*/
1335static int
1336TransferFunctionSwitchProc(
1337    ClientData clientData,      /* Flag indicating if the node is considered
1338                                 * before or after the insertion position. */
1339    Tcl_Interp *interp,         /* Interpreter to send results back to */
1340    const char *switchName,     /* Not used. */
1341    Tcl_Obj *objPtr,            /* String representation */
1342    char *record,               /* Structure record */
1343    int offset,                 /* Not used. */
1344    int flags)                  /* Not used. */
1345{
1346    TransferFunction **funcPtrPtr = (TransferFunction **)(record + offset);
1347    TransferFunction *funcPtr;
1348    funcPtr = NanoVis::get_transfunc(Tcl_GetString(objPtr));
1349    if (funcPtr == NULL) {
1350        Tcl_AppendResult(interp, "transfer function \"", Tcl_GetString(objPtr),
1351                         "\" is not defined", (char*)NULL);
1352        return TCL_ERROR;
1353    }
1354    *funcPtrPtr = funcPtr;
1355    return TCL_OK;
1356}
1357
1358/*
1359 *---------------------------------------------------------------------------
1360 *
1361 * VideoFormatSwitchProc --
1362 *
1363 *      Convert a Tcl_Obj representing the video format into its
1364 *      integer id.
1365 *
1366 * Results:
1367 *      The return value is a standard Tcl result.
1368 *
1369 *---------------------------------------------------------------------------
1370 */
1371/*ARGSUSED*/
1372static int
1373VideoFormatSwitchProc(
1374    ClientData clientData,      /* Not used. */
1375    Tcl_Interp *interp,         /* Interpreter to send results back to */
1376    const char *switchName,     /* Not used. */
1377    Tcl_Obj *objPtr,            /* String representation */
1378    char *record,               /* Structure record */
1379    int offset,                 /* Not used. */
1380    int flags)                  /* Not used. */
1381{
1382    Rappture::AVTranslate::VideoFormats *formatPtr =
1383        (Rappture::AVTranslate::VideoFormats *)(record + offset);
1384    const char *string;
1385    char c;
1386
1387    string = Tcl_GetString(objPtr);
1388    c = string[0];
1389    if ((c == 'm') && (strcmp(string, "mpeg") == 0)) {
1390        *formatPtr =  Rappture::AVTranslate::MPEG1;
1391    } else if ((c == 't') && (strcmp(string, "theora") == 0)) {
1392        *formatPtr = Rappture::AVTranslate::THEORA;
1393    } else if ((c == 'm') && (strcmp(string, "mov") == 0)) {
1394        *formatPtr = Rappture::AVTranslate::QUICKTIME;
1395    } else {
1396        Tcl_AppendResult(interp, "bad video format \"", string,
1397                     "\": should be mpeg, theora, or mov", (char*)NULL);
1398    }
1399    return TCL_ERROR;
1400}
1401
1402static int
1403FlowConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1404                Tcl_Obj *const *objv)
1405{
1406    FlowCmd *flowPtr = (FlowCmd *)clientData;
1407
1408    if (flowPtr->ParseSwitches(interp, objc - 2, objv + 2) != TCL_OK) {
1409        return TCL_ERROR;
1410    }
1411    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1412    return TCL_OK;
1413}
1414
1415static int
1416FlowParticlesAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1417                   Tcl_Obj *const *objv)
1418{
1419    FlowCmd *flowPtr = (FlowCmd *)clientData;
1420
1421    if (flowPtr->CreateParticles(interp, objv[3]) != TCL_OK) {
1422        return TCL_ERROR;
1423    }
1424    FlowParticles *particlesPtr;
1425    if (flowPtr->GetParticles(interp, objv[3], &particlesPtr) != TCL_OK) {
1426        return TCL_ERROR;
1427    }
1428    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1429        delete particlesPtr;
1430        return TCL_ERROR;
1431    }
1432    particlesPtr->Configure();
1433    NanoVis::EventuallyRedraw();
1434    Tcl_SetObjResult(interp, objv[3]);
1435    return TCL_OK;
1436}
1437
1438static int
1439FlowParticlesConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1440                         Tcl_Obj *const *objv)
1441{
1442    FlowCmd *flowPtr = (FlowCmd *)clientData;
1443
1444    FlowParticles *particlesPtr;
1445    if (flowPtr->GetParticles(interp, objv[3], &particlesPtr) != TCL_OK) {
1446        return TCL_ERROR;
1447    }
1448    if (particlesPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1449        return TCL_ERROR;
1450    }
1451    particlesPtr->Configure();
1452    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1453    return TCL_OK;
1454}
1455
1456static int
1457FlowParticlesDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1458                      Tcl_Obj *const *objv)
1459{
1460    FlowCmd *flowPtr = (FlowCmd *)clientData;
1461    int i;
1462    for (i = 3; i < objc; i++) {
1463        FlowParticles *particlesPtr;
1464
1465        if (flowPtr->GetParticles(NULL, objv[i], &particlesPtr) == TCL_OK) {
1466            delete particlesPtr;
1467        }
1468    }
1469    NanoVis::EventuallyRedraw();
1470    return TCL_OK;
1471}
1472
1473static int
1474FlowParticlesNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1475                     Tcl_Obj *const *objv)
1476{
1477    FlowCmd *flowPtr = (FlowCmd *)clientData;
1478    Tcl_Obj *listObjPtr;
1479    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1480    FlowParticlesIterator iter;
1481    FlowParticles *particlesPtr;
1482    for (particlesPtr = flowPtr->FirstParticles(&iter); particlesPtr != NULL;
1483         particlesPtr = flowPtr->NextParticles(&iter)) {
1484        Tcl_Obj *objPtr;
1485
1486        objPtr = Tcl_NewStringObj(particlesPtr->name(), -1);
1487        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1488    }
1489    Tcl_SetObjResult(interp, listObjPtr);
1490    return TCL_OK;
1491}
1492
1493/*
1494 *---------------------------------------------------------------------------
1495 *
1496 * FlowParticlesObjCmd --
1497 *
1498 *      This procedure is invoked to process commands on behalf of the flow
1499 *      object.
1500 *
1501 * Results:
1502 *      A standard Tcl result.
1503 *
1504 * Side effects:
1505 *      See the user documentation.
1506 *
1507 * $flow particles oper $name
1508 *---------------------------------------------------------------------------
1509 */
1510static Rappture::CmdSpec flowParticlesOps[] = {
1511    {"add",        1, FlowParticlesAddOp,        4, 0, "name ?switches?",},
1512    {"configure",  1, FlowParticlesConfigureOp,  4, 0, "name ?switches?",},
1513    {"delete",     1, FlowParticlesDeleteOp,     4, 0, "?name...?"},
1514    {"names",      1, FlowParticlesNamesOp,      3, 4, "?pattern?"},
1515};
1516
1517static int nFlowParticlesOps = NumCmdSpecs(flowParticlesOps);
1518
1519static int
1520FlowParticlesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1521               Tcl_Obj *const *objv)
1522{
1523    Tcl_ObjCmdProc *proc;
1524    proc = Rappture::GetOpFromObj(interp, nFlowParticlesOps, flowParticlesOps,
1525        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1526    if (proc == NULL) {
1527        return TCL_ERROR;
1528    }
1529    FlowCmd *flowPtr = (FlowCmd *)clientData;
1530    Tcl_Preserve(flowPtr);
1531    int result;
1532    result = (*proc) (clientData, interp, objc, objv);
1533    Tcl_Release(flowPtr);
1534    return result;
1535}
1536
1537static int
1538FlowBoxAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1539               Tcl_Obj *const *objv)
1540{
1541    FlowCmd *flowPtr = (FlowCmd *)clientData;
1542
1543    if (flowPtr->CreateBox(interp, objv[3]) != TCL_OK) {
1544        return TCL_ERROR;
1545    }
1546    FlowBox *boxPtr;
1547    if (flowPtr->GetBox(interp, objv[3], &boxPtr) != TCL_OK) {
1548        return TCL_ERROR;
1549    }
1550    if (boxPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1551        delete boxPtr;
1552        return TCL_ERROR;
1553    }
1554    NanoVis::EventuallyRedraw();
1555    Tcl_SetObjResult(interp, objv[3]);
1556    return TCL_OK;
1557}
1558
1559static int
1560FlowBoxDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1561                   Tcl_Obj *const *objv)
1562{
1563    FlowCmd *flowPtr = (FlowCmd *)clientData;
1564    int i;
1565    for (i = 3; i < objc; i++) {
1566        FlowBox *boxPtr;
1567
1568        if (flowPtr->GetBox(NULL, objv[i], &boxPtr) == TCL_OK) {
1569            delete boxPtr;
1570        }
1571    }
1572    NanoVis::EventuallyRedraw();
1573    return TCL_OK;
1574}
1575
1576static int
1577FlowBoxNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1578             Tcl_Obj *const *objv)
1579{
1580    FlowCmd *flowPtr = (FlowCmd *)clientData;
1581    Tcl_Obj *listObjPtr;
1582    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1583    FlowBoxIterator iter;
1584    FlowBox *boxPtr;
1585    for (boxPtr = flowPtr->FirstBox(&iter); boxPtr != NULL;
1586         boxPtr = flowPtr->NextBox(&iter)) {
1587        Tcl_Obj *objPtr;
1588
1589        objPtr = Tcl_NewStringObj(boxPtr->name(), -1);
1590        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1591    }
1592    Tcl_SetObjResult(interp, listObjPtr);
1593    return TCL_OK;
1594}
1595
1596static int
1597FlowBoxConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1598                        Tcl_Obj *const *objv)
1599{
1600    FlowCmd *flowPtr = (FlowCmd *)clientData;
1601
1602    FlowBox *boxPtr;
1603    if (flowPtr->GetBox(interp, objv[3], &boxPtr) != TCL_OK) {
1604        return TCL_ERROR;
1605    }
1606    if (boxPtr->ParseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
1607        return TCL_ERROR;
1608    }
1609    NanoVis::EventuallyRedraw();
1610    return TCL_OK;
1611}
1612
1613/*
1614 *---------------------------------------------------------------------------
1615 *
1616 * FlowBoxOp--
1617 *
1618 *      This procedure is invoked to process commands on behalf of the flow
1619 *      object.
1620 *
1621 * Results:
1622 *      A standard Tcl result.
1623 *
1624 * Side effects:
1625 *      See the user documentation.
1626 *
1627 *---------------------------------------------------------------------------
1628 */
1629static Rappture::CmdSpec flowBoxOps[] = {
1630    {"add",        1, FlowBoxAddOp,        4, 0, "name ?switches?",},
1631    {"configure",  1, FlowBoxConfigureOp,  4, 0, "name ?switches?",},
1632    {"delete",     1, FlowBoxDeleteOp,     3, 0, "?name...?"},
1633    {"names",      1, FlowBoxNamesOp,      3, 0, "?pattern?"},
1634};
1635
1636static int nFlowBoxOps = NumCmdSpecs(flowBoxOps);
1637
1638static int
1639FlowBoxOp(ClientData clientData, Tcl_Interp *interp, int objc,
1640               Tcl_Obj *const *objv)
1641{
1642    Tcl_ObjCmdProc *proc;
1643    proc = Rappture::GetOpFromObj(interp, nFlowBoxOps, flowBoxOps,
1644        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1645    if (proc == NULL) {
1646        return TCL_ERROR;
1647    }
1648    FlowCmd *flowPtr = (FlowCmd *)clientData;
1649    Tcl_Preserve(flowPtr);
1650    int result;
1651    result = (*proc) (clientData, interp, objc, objv);
1652    Tcl_Release(flowPtr);
1653    return result;
1654}
1655
1656/*
1657 * ----------------------------------------------------------------------
1658 * CLIENT COMMAND:
1659 *   $flow legend <width> <height>
1660 *
1661 * Clients use this to generate a legend image for the specified
1662 * transfer function.  The legend image is a color gradient from 0
1663 * to one, drawn in the given transfer function.  The resulting image
1664 * is returned in the size <width> x <height>.
1665 * ----------------------------------------------------------------------
1666 */
1667static int
1668FlowLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
1669          Tcl_Obj *const *objv)
1670{
1671    FlowCmd *flowPtr = (FlowCmd *)clientData;
1672   
1673    const char *string = Tcl_GetString(objv[1]);
1674    TransferFunction *tf;
1675    tf = flowPtr->GetTransferFunction();
1676    if (tf == NULL) {
1677        Tcl_AppendResult(interp, "unknown transfer function \"", string, "\"",
1678                             (char*)NULL);
1679        return TCL_ERROR;
1680    }
1681    const char *label;
1682    label = Tcl_GetString(objv[0]);
1683    int w, h;
1684    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
1685        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
1686        return TCL_ERROR;
1687    }
1688    if (NanoVis::flags & NanoVis::MAP_FLOWS) {
1689        NanoVis::MapFlows();
1690    }
1691    NanoVis::render_legend(tf, NanoVis::magMin, NanoVis::magMax, w, h, label);
1692    return TCL_OK;
1693}
1694
1695/*
1696 *---------------------------------------------------------------------------
1697 *
1698 * FlowInstObjCmd --
1699 *
1700 *      This procedure is invoked to process commands on behalf of the flow
1701 *      object.
1702 *
1703 * Results:
1704 *      A standard Tcl result.
1705 *
1706 * Side effects:
1707 *      See the user documentation.
1708 *
1709 *---------------------------------------------------------------------------
1710 */
1711static Rappture::CmdSpec flowInstOps[] = {
1712    {"box",         1, FlowBoxOp,        2, 0, "oper ?args?"},
1713    {"configure",   1, FlowConfigureOp,  2, 0, "?switches?"},
1714    {"data",        1, FlowDataOp,       2, 0, "oper ?args?"},
1715    {"legend",      1, FlowLegendOp,     4, 4, "w h"},
1716    {"particles",   1, FlowParticlesOp,  2, 0, "oper ?args?"}
1717};
1718static int nFlowInstOps = NumCmdSpecs(flowInstOps);
1719
1720static int
1721FlowInstObjCmd(ClientData clientData, Tcl_Interp *interp, int objc,
1722               Tcl_Obj *const *objv)
1723{
1724    Tcl_ObjCmdProc *proc;
1725    proc = Rappture::GetOpFromObj(interp, nFlowInstOps, flowInstOps,
1726        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1727    if (proc == NULL) {
1728        return TCL_ERROR;
1729    }
1730    assert(CheckGL(AT));
1731    FlowCmd *flowPtr = (FlowCmd *)clientData;
1732    Tcl_Preserve(flowPtr);
1733    int result;
1734    result = (*proc) (clientData, interp, objc, objv);
1735    Tcl_Release(flowPtr);
1736    return result;
1737}
1738
1739/*
1740 *---------------------------------------------------------------------------
1741 *
1742 * FlowInstDeleteProc --
1743 *
1744 *      Deletes the command associated with the tree.  This is called only
1745 *      when the command associated with the tree is destroyed.
1746 *
1747 * Results:
1748 *      None.
1749 *
1750 *---------------------------------------------------------------------------
1751 */
1752static void
1753FlowInstDeleteProc(ClientData clientData)
1754{
1755    FlowCmd *flowPtr = (FlowCmd *)clientData;
1756    delete flowPtr;
1757}
1758
1759/*
1760 *---------------------------------------------------------------------------
1761 *
1762 * FlowAddOp --
1763 *
1764 *---------------------------------------------------------------------------
1765 */
1766/*ARGSUSED*/
1767static int
1768FlowAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1769          Tcl_Obj *const *objv)
1770{
1771    if (NanoVis::CreateFlow(interp, objv[2]) != TCL_OK) {
1772        return TCL_ERROR;
1773    }
1774    FlowCmd *flowPtr;
1775    if (NanoVis::GetFlow(interp, objv[2], &flowPtr) != TCL_OK) {
1776        return TCL_ERROR;
1777    }
1778    if (flowPtr->ParseSwitches(interp, objc - 3, objv + 3) != TCL_OK) {
1779        Tcl_DeleteCommand(interp, flowPtr->name());
1780        return TCL_ERROR;
1781    }
1782    Tcl_SetObjResult(interp, objv[2]);
1783    NanoVis::EventuallyRedraw();
1784    return TCL_OK;
1785}
1786
1787/*
1788 *---------------------------------------------------------------------------
1789 *
1790 * FlowDeleteOp --
1791 *
1792 *---------------------------------------------------------------------------
1793 */
1794/*ARGSUSED*/
1795static int
1796FlowDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1797             Tcl_Obj *const *objv)
1798{
1799    int i;
1800
1801    for (i = 2; i < objc; i++) {
1802        FlowCmd *flowPtr;
1803
1804        if (NanoVis::GetFlow(interp, objv[i], &flowPtr) != TCL_OK) {
1805            return TCL_ERROR;
1806        }
1807        Tcl_DeleteCommand(interp, flowPtr->name());
1808    }
1809    NanoVis::EventuallyRedraw(NanoVis::MAP_FLOWS);
1810    return TCL_OK;
1811}
1812
1813/*
1814 *---------------------------------------------------------------------------
1815 *
1816 * FlowExistsOp --
1817 *
1818 *---------------------------------------------------------------------------
1819 */
1820/*ARGSUSED*/
1821static int
1822FlowExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1823             Tcl_Obj *const *objv)
1824{
1825    bool value;
1826    FlowCmd *flowPtr;
1827
1828    value = false;
1829    if (NanoVis::GetFlow(NULL, objv[2], &flowPtr) == TCL_OK) {
1830        value = true;
1831    }
1832    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
1833    return TCL_OK;
1834}
1835
1836/*
1837 *---------------------------------------------------------------------------
1838 *
1839 * FlowGotoOp --
1840 *
1841 *      flow goto number
1842 *
1843 *---------------------------------------------------------------------------
1844 */
1845static int
1846FlowGotoOp(ClientData clientData, Tcl_Interp *interp, int objc,
1847             Tcl_Obj *const *objv)
1848{
1849    int nSteps;
1850    if (Tcl_GetIntFromObj(interp, objv[2], &nSteps) != TCL_OK) {
1851        return TCL_ERROR;
1852    }
1853    if ((nSteps < 0) || (nSteps > SHRT_MAX)) {
1854        Tcl_AppendResult(interp, "flow goto: bad # of steps \"",
1855                         Tcl_GetString(objv[2]), "\"", (char *)NULL);
1856        return TCL_ERROR;
1857    }
1858    NanoVis::ResetFlows();
1859    if (NanoVis::flags & NanoVis::MAP_FLOWS) {
1860        NanoVis::MapFlows();
1861    }
1862    int i;
1863    NanoVis::AdvectFlows();
1864    for (i = 0; i < nSteps; i++) {
1865        if (NanoVis::licRenderer->active()) {
1866            NanoVis::licRenderer->convolve();
1867        }
1868        NanoVis::AdvectFlows();
1869    }
1870    NanoVis::EventuallyRedraw();
1871    return TCL_OK;
1872}
1873
1874/*
1875 *---------------------------------------------------------------------------
1876 *
1877 * FlowNamesOp --
1878 *
1879 *---------------------------------------------------------------------------
1880 */
1881/*ARGSUSED*/
1882static int
1883FlowNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1884            Tcl_Obj *const *objv)
1885{
1886    Tcl_Obj *listObjPtr;
1887    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1888    FlowCmd *flowPtr;
1889    FlowIterator iter;
1890    for (flowPtr = NanoVis::FirstFlow(&iter); flowPtr != NULL;
1891         flowPtr = NanoVis::NextFlow(&iter)) {
1892        Tcl_Obj *objPtr;
1893
1894        objPtr = Tcl_NewStringObj(flowPtr->name(), -1);
1895        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1896    }
1897    Tcl_SetObjResult(interp, listObjPtr);
1898    return TCL_OK;
1899}
1900
1901static int
1902FlowNextOp(ClientData clientData, Tcl_Interp *interp, int objc,
1903             Tcl_Obj *const *objv)
1904{
1905    assert(NanoVis::licRenderer != NULL);
1906    if (NanoVis::flags & NanoVis::MAP_FLOWS) {
1907        NanoVis::MapFlows();
1908    }
1909    NanoVis::EventuallyRedraw();
1910    NanoVis::licRenderer->convolve();
1911    NanoVis::AdvectFlows();
1912    return TCL_OK;
1913}
1914
1915static int
1916FlowResetOp(ClientData clientData, Tcl_Interp *interp, int objc,
1917             Tcl_Obj *const *objv)
1918{
1919    NanoVis::ResetFlows();
1920    return TCL_OK;
1921}
1922
1923struct FlowVideoValues {
1924    float frameRate;                    /* Frame rate */
1925    int bitRate;                        /* Video bitrate */
1926    int width, height;                  /* Dimensions of video frame. */
1927    int nFrames;
1928    Rappture::AVTranslate::VideoFormats format;
1929};
1930
1931static Rappture::SwitchParseProc VideoFormatSwitchProc;
1932static Rappture::SwitchCustom videoFormatSwitch = {
1933    VideoFormatSwitchProc, NULL, 0,
1934};
1935
1936Rappture::SwitchSpec FlowCmd::videoSwitches[] = {
1937    {Rappture::SWITCH_FLOAT, "-bitrate", "value",
1938        offsetof(FlowVideoValues, bitRate), 0},
1939    {Rappture::SWITCH_CUSTOM, "-format", "string",
1940        offsetof(FlowVideoValues, format), 0, 0, &videoFormatSwitch},
1941    {Rappture::SWITCH_FLOAT, "-framerate", "value",
1942        offsetof(FlowVideoValues, frameRate), 0},
1943    {Rappture::SWITCH_INT, "-height", "integer",
1944        offsetof(FlowVideoValues, height), 0},
1945    {Rappture::SWITCH_INT, "-numframes", "count",
1946        offsetof(FlowVideoValues, nFrames), 0},
1947    {Rappture::SWITCH_INT, "-width", "integer",
1948        offsetof(FlowVideoValues, width), 0},
1949    {Rappture::SWITCH_END}
1950};
1951
1952static int
1953FlowVideoOp(ClientData clientData, Tcl_Interp *interp, int objc,
1954            Tcl_Obj *const *objv)
1955{
1956    struct pollfd pollResults;
1957    int timeout;
1958
1959    pollResults.fd = fileno(NanoVis::stdin);
1960    pollResults.events = POLLIN;
1961
1962#define PENDING_TIMEOUT         10  /* milliseconds. */
1963    timeout = PENDING_TIMEOUT;
1964
1965    FlowVideoValues values;
1966    const char *token;
1967
1968    token = Tcl_GetString(objv[2]);
1969    values.frameRate = 25.0f;           // Default frame rate 25 fps
1970    values.bitRate = 6.0e+6f;           // Default video bit rate.
1971    values.width = NanoVis::win_width;
1972    values.height = NanoVis::win_height;
1973    values.nFrames = 100;
1974    values.format = Rappture::AVTranslate::MPEG1;
1975    if (Rappture::ParseSwitches(interp, FlowCmd::videoSwitches,
1976        objc - 3, objv + 3, &values, SWITCH_DEFAULTS) < 0) {
1977        return TCL_ERROR;
1978    }
1979    if ((values.width < 0) || (values.width > SHRT_MAX) ||
1980        (values.height < 0) || (values.height > SHRT_MAX)) {
1981        Tcl_AppendResult(interp, "bad dimensions for video", (char *)NULL);
1982        return TCL_ERROR;
1983    }
1984    if ((values.frameRate < 0.0f) || (values.frameRate > 30.0f)) {
1985        Tcl_AppendResult(interp, "bad frame rate.", (char *)NULL);
1986        return TCL_ERROR;
1987    }
1988    if (values.bitRate < 0.0f) {
1989        Tcl_AppendResult(interp, "bad bit rate.", (char *)NULL);
1990        return TCL_ERROR;
1991    }
1992    if (NanoVis::licRenderer == NULL) {
1993        Tcl_AppendResult(interp, "no lic renderer.", (char *)NULL);
1994        return TCL_ERROR;
1995    }
1996    // Save the old dimensions of the offscreen buffer.
1997    int oldWidth, oldHeight;
1998    oldWidth = NanoVis::win_width;
1999    oldHeight = NanoVis::win_height;
2000
2001    Trace("FLOW started\n");
2002
2003    Rappture::Outcome context;
2004
2005    Rappture::AVTranslate movie(values.width, values.height, values.bitRate,
2006        values.frameRate);
2007    char tmpFileName[200];
2008    sprintf(tmpFileName,"/tmp/flow%d.mpeg", getpid());
2009    if (!movie.init(context, tmpFileName)) {
2010        Tcl_AppendResult(interp, "can't initialized movie \"", tmpFileName,
2011                "\": ", context.remark(), (char *)NULL);
2012        return TCL_ERROR;
2013    }
2014    if ((values.width != oldWidth) || (values.height != oldHeight)) {
2015        // Resize to the requested size.
2016        NanoVis::resize_offscreen_buffer(values.width, values.height);
2017    }
2018    // Now compute the line padding for the offscreen buffer.
2019    int pad = 0;
2020    if (( 3 * values.width) % 4 > 0) {
2021        pad = 4 - ((3* values.width) % 4);
2022    }
2023    NanoVis::ResetFlows();
2024    bool canceled = false;
2025    for (int i = 1; i <= values.nFrames; i++) {
2026        if (((i & 0xF) == 0) && (poll(&pollResults, 1, 0) > 0)) {
2027            /* If there's another command on stdin, that means the client is
2028             * trying to cancel this operation. */
2029            canceled = true;
2030            break;
2031        }
2032        if (NanoVis::licRenderer->active()) {
2033            NanoVis::licRenderer->convolve();
2034        }
2035        NanoVis::AdvectFlows();
2036        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, final_fbo);
2037        NanoVis::display();
2038        NanoVis::read_screen();
2039        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
2040        movie.append(context, NanoVis::screen_buffer, pad);
2041    }
2042    movie.done(context);
2043    Trace("FLOW end\n");
2044    if (!canceled) {
2045        Rappture::Buffer data;
2046
2047        /* FIXME: find a way to get the data from the movie object as a
2048         * void* */
2049        if (!data.load(context, tmpFileName)) {
2050            Tcl_AppendResult(interp, "can't load data from temporary file \"",
2051                tmpFileName, "\": ", context.remark(), (char *)NULL);
2052            return TCL_ERROR;
2053        }
2054
2055        char command[200];
2056        sprintf(command,"nv>image -bytes %lu -type movie -token \"%s\"\n",
2057                (unsigned long)data.size(), token);
2058        NanoVis::sendDataToClient(command, data.bytes(), data.size());
2059    }
2060    if ((values.width != oldWidth) || (values.height != oldHeight)) {
2061        NanoVis::resize_offscreen_buffer(oldWidth, oldHeight);
2062    }
2063    NanoVis::ResetFlows();
2064    if (unlink(tmpFileName) != 0) {
2065        Tcl_AppendResult(interp, "can't unlink temporary movie file \"",
2066                tmpFileName, "\": ", Tcl_PosixError(interp), (char *)NULL);
2067        return TCL_ERROR;
2068    }
2069    return TCL_OK;
2070}
2071
2072
2073/*
2074 *---------------------------------------------------------------------------
2075 *
2076 * FlowObjCmd --
2077 *
2078 *---------------------------------------------------------------------------
2079 */
2080static Rappture::CmdSpec flowCmdOps[] = {
2081    {"add",      1, FlowAddOp,     3, 0, "name ?option value...?",},
2082    {"delete",   1, FlowDeleteOp,  2, 0, "name...",},
2083    {"exists",   1, FlowExistsOp,  3, 3, "name",},
2084    {"goto",     1, FlowGotoOp,    3, 3, "nSteps",},
2085    {"names",    1, FlowNamesOp,   2, 3, "?pattern?",},
2086    {"next",     2, FlowNextOp,    2, 2, "",},
2087    {"reset",    1, FlowResetOp,   2, 2, "",},
2088    {"video",    1, FlowVideoOp,   3, 0, "token ?switches...?",},
2089};
2090static int nFlowCmdOps = NumCmdSpecs(flowCmdOps);
2091
2092/*ARGSUSED*/
2093static int
2094FlowCmdProc(ClientData clientData, Tcl_Interp *interp, int objc,
2095            Tcl_Obj *const *objv)
2096{
2097    Tcl_ObjCmdProc *proc;
2098
2099    proc = Rappture::GetOpFromObj(interp, nFlowCmdOps, flowCmdOps,
2100        Rappture::CMDSPEC_ARG1, objc, objv, 0);
2101    if (proc == NULL) {
2102        return TCL_ERROR;
2103    }
2104    return (*proc) (clientData, interp, objc, objv);
2105}
2106
2107/*
2108 *---------------------------------------------------------------------------
2109 *
2110 * FlowCmdInitProc --
2111 *
2112 *      This procedure is invoked to initialize the "tree" command.
2113 *
2114 * Results:
2115 *      None.
2116 *
2117 * Side effects:
2118 *      Creates the new command and adds a new entry into a global Tcl
2119 *      associative array.
2120 *
2121 *---------------------------------------------------------------------------
2122 */
2123int
2124FlowCmdInitProc(Tcl_Interp *interp)
2125{
2126    Tcl_CreateObjCommand(interp, "flow", FlowCmdProc, NULL, NULL);
2127    NanoVis::InitFlows();
2128    return TCL_OK;
2129}
2130
Note: See TracBrowser for help on using the repository browser.