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

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

Initial commit of new flow visualization command structure

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