source: branches/blt4/packages/vizservers/nanovis/FlowCmd.cpp @ 1829

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