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

Last change on this file since 3492 was 3492, checked in by ldelgass, 11 years ago

Fix camera reset for nanovis. Includes refactoring of vector/matrix classes
in nanovis to consolidate into vrmath library. Also add preliminary canonical
view control to clients for testing.

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