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

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