source: nanovis/branches/1.1/Command.cpp @ 4819

Last change on this file since 4819 was 4819, checked in by ldelgass, 5 years ago

merge r4067 from trunk

  • 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 * ----------------------------------------------------------------------
4 * Command.cpp
5 *
6 *      This modules creates the Tcl interface to the nanovis server.  The
7 *      communication protocol of the server is the Tcl language.  Commands
8 *      given to the server by clients are executed in a safe interpreter and
9 *      the resulting image rendered offscreen is returned as BMP-formatted
10 *      image data.
11 *
12 * ======================================================================
13 *  AUTHOR:  Wei Qiao <qiaow@purdue.edu>
14 *           Michael McLennan <mmclennan@purdue.edu>
15 *           Purdue Rendering and Perceptualization Lab (PURPL)
16 *
17 *  Copyright (c) 2004-2013  HUBzero Foundation, LLC
18 *
19 *  See the file "license.terms" for information on usage and
20 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 * ======================================================================
22 */
23
24/*
25 * TODO:  In no particular order...
26 *        o Use Tcl command option parser to reduce size of procedures, remove
27 *          lots of extra error checking code. (almost there)
28 *        o Add bookkeeping for volumes, heightmaps, flows, etc. to track
29 *          1) simulation # 2) include/exclude.  The include/exclude
30 *          is to indicate whether the item should contribute to the overall
31 *          limits of the axes.
32 */
33
34#include <assert.h>
35#include <stdlib.h>
36#include <unistd.h>                     /* Needed for getpid, gethostname,
37                                         * write, etc. */
38#include <tcl.h>
39
40#include <RpEncode.h>
41#include <RpOutcome.h>
42#include <RpBuffer.h>
43
44#include <vrmath/Vector3f.h>
45
46#include "nanovis.h"
47#include "CmdProc.h"
48#include "FlowCmd.h"
49#ifdef USE_POINTSET_RENDERER
50#include "PointSet.h"
51#endif
52#include "dxReader.h"
53#include "VtkReader.h"
54#include "Grid.h"
55#include "HeightMap.h"
56#include "NvCamera.h"
57#include "NvZincBlendeReconstructor.h"
58#include "Unirect.h"
59#include "Volume.h"
60#include "VolumeRenderer.h"
61#include "Trace.h"
62
63using namespace nv::graphics;
64using namespace vrmath;
65
66// default transfer function
67static const char def_transfunc[] =
68    "transfunc define default {\n\
69  0.00  0 0 1\n\
70  0.25  0 1 1\n\
71  0.50  0 1 0\n\
72  0.75  1 1 0\n\
73  1.00  1 0 0\n\
74} {\n\
75  0.000000 0.0\n\
76  0.107847 0.0\n\
77  0.107857 1.0\n\
78  0.177857 1.0\n\
79  0.177867 0.0\n\
80  0.250704 0.0\n\
81  0.250714 1.0\n\
82  0.320714 1.0\n\
83  0.320724 0.0\n\
84  0.393561 0.0\n\
85  0.393571 1.0\n\
86  0.463571 1.0\n\
87  0.463581 0.0\n\
88  0.536419 0.0\n\
89  0.536429 1.0\n\
90  0.606429 1.0\n\
91  0.606439 0.0\n\
92  0.679276 0.0\n\
93  0.679286 1.0\n\
94  0.749286 1.0\n\
95  0.749296 0.0\n\
96  0.822133 0.0\n\
97  0.822143 1.0\n\
98  0.892143 1.0\n\
99  0.892153 0.0\n\
100  1.000000 0.0\n\
101}";
102
103bool
104GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
105{
106    int value;
107
108    if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) {
109        return TCL_ERROR;
110    }
111    *boolPtr = (bool)value;
112    return TCL_OK;
113}
114
115int
116GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
117{
118    double value;
119
120    if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
121        return TCL_ERROR;
122    }
123    *valuePtr = (float)value;
124    return TCL_OK;
125}
126
127static int
128GetCullMode(Tcl_Interp *interp, Tcl_Obj *objPtr,
129            RenderContext::CullMode *modePtr)
130{
131    const char *string = Tcl_GetString(objPtr);
132    if (strcmp(string, "none") == 0) {
133        *modePtr = RenderContext::NO_CULL;
134    } else if (strcmp(string, "front") == 0) {
135        *modePtr = RenderContext::FRONT;
136    } else if (strcmp(string, "back") == 0) {
137        *modePtr = RenderContext::BACK;
138    } else {
139        Tcl_AppendResult(interp, "invalid cull mode \"", string,
140                         "\": should be front, back, or none\"", (char *)NULL);
141        return TCL_ERROR;
142    }
143    return TCL_OK;
144}
145
146static int
147GetShadingModel(Tcl_Interp *interp, Tcl_Obj *objPtr,
148                RenderContext::ShadingModel *modelPtr)
149{
150    const char *string = Tcl_GetString(objPtr);
151
152    if (strcmp(string,"flat") == 0) {
153        *modelPtr = RenderContext::FLAT;
154    } else if (strcmp(string,"smooth") == 0) {
155        *modelPtr = RenderContext::SMOOTH;
156    } else {
157        Tcl_AppendResult(interp, "bad shading model \"", string,
158                         "\": should be flat or smooth", (char *)NULL);
159        return TCL_ERROR;
160    }
161    return TCL_OK;
162}
163
164static int
165GetPolygonMode(Tcl_Interp *interp, Tcl_Obj *objPtr,
166               RenderContext::PolygonMode *modePtr)
167{
168    const char *string = Tcl_GetString(objPtr);
169
170    if (strcmp(string,"wireframe") == 0) {
171        *modePtr = RenderContext::LINE;
172    } else if (strcmp(string,"fill") == 0) {
173        *modePtr = RenderContext::FILL;
174    } else {
175        Tcl_AppendResult(interp, "invalid polygon mode \"", string,
176                         "\": should be wireframe or fill\"", (char *)NULL);
177        return TCL_ERROR;
178    }
179    return TCL_OK;
180}
181
182/**
183 * Creates a heightmap from the given the data. The format of the data
184 * should be as follows:
185 *
186 *     xMin, xMax, xNum, yMin, yMax, yNum, heights...
187 *
188 * xNum and yNum must be integer values, all others are real numbers.
189 * The number of heights must be xNum * yNum;
190 */
191static HeightMap *
192CreateHeightMap(ClientData clientData, Tcl_Interp *interp, int objc,
193                Tcl_Obj *const *objv)
194{
195    float xMin, yMin, xMax, yMax;
196    int xNum, yNum;
197
198    if (objc != 7) {
199        Tcl_AppendResult(interp,
200        "wrong # of values: should be xMin yMin xMax yMax xNum yNum heights",
201        (char *)NULL);
202        return NULL;
203    }
204    if ((GetFloatFromObj(interp, objv[0], &xMin) != TCL_OK) ||
205        (GetFloatFromObj(interp, objv[1], &yMin) != TCL_OK) ||
206        (GetFloatFromObj(interp, objv[2], &xMax) != TCL_OK) ||
207        (GetFloatFromObj(interp, objv[3], &yMax) != TCL_OK) ||
208        (Tcl_GetIntFromObj(interp, objv[4], &xNum) != TCL_OK) ||
209        (Tcl_GetIntFromObj(interp, objv[5], &yNum) != TCL_OK)) {
210        return NULL;
211    }
212    int nValues;
213    Tcl_Obj **elem;
214    if (Tcl_ListObjGetElements(interp, objv[6], &nValues, &elem) != TCL_OK) {
215        return NULL;
216    }
217    if ((xNum <= 0) || (yNum <= 0)) {
218        Tcl_AppendResult(interp, "bad number of x or y values", (char *)NULL);
219        return NULL;
220    }
221    if (nValues != (xNum * yNum)) {
222        Tcl_AppendResult(interp, "wrong # of heights", (char *)NULL);
223        return NULL;
224    }
225
226    float *heights;
227    heights = new float[nValues];
228    if (heights == NULL) {
229        Tcl_AppendResult(interp, "can't allocate array of heights",
230                         (char *)NULL);
231        return NULL;
232    }
233
234    int i;
235    for (i = 0; i < nValues; i++) {
236        if (GetFloatFromObj(interp, elem[i], heights + i) != TCL_OK) {
237            delete [] heights;
238            return NULL;
239        }
240    }
241    HeightMap *heightMap = new HeightMap();
242    heightMap->setHeight(xMin, yMin, xMax, yMax, xNum, yNum, heights);
243    heightMap->transferFunction(NanoVis::getTransferFunction("default"));
244    heightMap->setVisible(true);
245    heightMap->setLineContourVisible(true);
246    delete [] heights;
247    return heightMap;
248}
249
250static int
251GetHeightMapFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, HeightMap **hmPtrPtr)
252{
253    const char *string = Tcl_GetString(objPtr);
254
255    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(string);
256    if (itr == NanoVis::heightMapTable.end()) {
257        if (interp != NULL) {
258            Tcl_AppendResult(interp, "can't find a heightmap named \"",
259                         string, "\"", (char*)NULL);
260        }
261        return TCL_ERROR;
262    }
263    *hmPtrPtr = itr->second;
264    return TCL_OK;
265}
266
267/**
268 * Used internally to decode a series of volume index values and
269 * store then in the specified vector.  If there are no volume index
270 * arguments, this means "all volumes" to most commands, so all
271 * active volume indices are stored in the vector.
272 *
273 * Updates pushes index values into the vector.  Returns TCL_OK or
274 * TCL_ERROR to indicate an error.
275 */
276static int
277GetVolumeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Volume **volPtrPtr)
278{
279    const char *string = Tcl_GetString(objPtr);
280
281    NanoVis::VolumeHashmap::iterator itr = NanoVis::volumeTable.find(string);
282    if (itr == NanoVis::volumeTable.end()) {
283        if (interp != NULL) {
284            Tcl_AppendResult(interp, "can't find a volume named \"",
285                         string, "\"", (char*)NULL);
286        }
287        return TCL_ERROR;
288    }
289    *volPtrPtr = itr->second;
290    return TCL_OK;
291}
292
293/**
294 * Used internally to decode a series of volume index values and
295 * store then in the specified vector.  If there are no volume index
296 * arguments, this means "all volumes" to most commands, so all
297 * active volume indices are stored in the vector.
298 *
299 * Updates pushes index values into the vector.  Returns TCL_OK or
300 * TCL_ERROR to indicate an error.
301 */
302static int
303GetVolumes(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
304           std::vector<Volume *>* vectorPtr)
305{
306    if (objc == 0) {
307        // No arguments. Get all volumes.
308        NanoVis::VolumeHashmap::iterator itr;
309        for (itr = NanoVis::volumeTable.begin();
310             itr != NanoVis::volumeTable.end(); ++itr) {
311            vectorPtr->push_back(itr->second);
312        }
313    } else {
314        // Get the volumes associated with the given index arguments.
315        for (int n = 0; n < objc; n++) {
316            Volume *volume;
317            if (GetVolumeFromObj(interp, objv[n], &volume) != TCL_OK) {
318                return TCL_ERROR;
319            }
320            vectorPtr->push_back(volume);
321        }
322    }
323    return TCL_OK;
324}
325
326/**
327 * Used internally to decode a series of volume index values and
328 * store then in the specified vector.  If there are no volume index
329 * arguments, this means "all volumes" to most commands, so all
330 * active volume indices are stored in the vector.
331 *
332 * Updates pushes index values into the vector.  Returns TCL_OK or
333 * TCL_ERROR to indicate an error.
334 */
335static int
336GetHeightMaps(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
337              std::vector<HeightMap *>* vectorPtr)
338{
339    if (objc == 0) {
340        // No arguments. Get all heightmaps.
341        NanoVis::HeightMapHashmap::iterator itr;
342        for (itr = NanoVis::heightMapTable.begin();
343             itr != NanoVis::heightMapTable.end(); ++itr) {
344            vectorPtr->push_back(itr->second);
345        }
346    } else {
347        for (int n = 0; n < objc; n++) {
348            HeightMap *heightMap;
349            if (GetHeightMapFromObj(interp, objv[n], &heightMap) != TCL_OK) {
350                return TCL_ERROR;
351            }
352            vectorPtr->push_back(heightMap);
353        }
354    }
355    return TCL_OK;
356}
357
358/**
359 * Used internally to decode an axis value from a string ("x", "y",
360 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
361 * along with a value in valPtr.  Otherwise, it returns TCL_ERROR
362 * and an error message in the interpreter.
363 */
364static int
365GetAxis(Tcl_Interp *interp, const char *string, int *indexPtr)
366{
367    if (string[1] == '\0') {
368        char c;
369
370        c = tolower((unsigned char)string[0]);
371        if (c == 'x') {
372            *indexPtr = 0;
373            return TCL_OK;
374        } else if (c == 'y') {
375            *indexPtr = 1;
376            return TCL_OK;
377        } else if (c == 'z') {
378            *indexPtr = 2;
379            return TCL_OK;
380        }
381        /*FALLTHRU*/
382    }
383    Tcl_AppendResult(interp, "bad axis \"", string,
384                     "\": should be x, y, or z", (char*)NULL);
385    return TCL_ERROR;
386}
387
388/**
389 * Used internally to decode an axis value from a string ("x", "y",
390 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
391 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
392 * and an error message in the interpreter.
393 */
394int
395GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr)
396{
397    return GetAxis(interp, Tcl_GetString(objPtr), indexPtr);
398}
399
400/**
401 * Used internally to decode an axis value from a string ("x", "y",
402 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
403 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
404 * and an error message in the interpreter.
405 */
406static int
407GetAxisDirFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr, int *dirPtr)
408{
409    const char *string = Tcl_GetString(objPtr);
410
411    int sign = 1;
412    if (*string == '-') {
413        sign = -1;
414        string++;
415    }
416    if (GetAxis(interp, string, indexPtr) != TCL_OK) {
417        return TCL_ERROR;
418    }
419    if (dirPtr != NULL) {
420        *dirPtr = sign;
421    }
422    return TCL_OK;
423}
424
425/**
426 * Used internally to decode a color value from a string ("R G B")
427 * as a list of three numbers 0-1.  Returns TCL_OK if successful,
428 * along with RGB values in rgbPtr.  Otherwise, it returns TCL_ERROR
429 * and an error message in the interpreter.
430 */
431static int
432GetColor(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, float *rgbPtr)
433{
434    if (objc < 3) {
435        Tcl_AppendResult(interp, "missing color values\": ",
436                         "should list of R G B values 0.0 - 1.0", (char*)NULL);
437        return TCL_ERROR;
438    }
439    if ((GetFloatFromObj(interp, objv[0], rgbPtr + 0) != TCL_OK) ||
440        (GetFloatFromObj(interp, objv[1], rgbPtr + 1) != TCL_OK) ||
441        (GetFloatFromObj(interp, objv[2], rgbPtr + 2) != TCL_OK)) {
442        return TCL_ERROR;
443    }
444    return TCL_OK;
445}
446
447/**
448 * Read the requested number of bytes from standard input into the given
449 * buffer.  The buffer is then decompressed and decoded.
450 */
451int
452GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes)
453{
454    char buffer[8096];
455
456    clearerr(NanoVis::stdin);
457    while (nBytes > 0) {
458        unsigned int chunk;
459        int nRead;
460
461        chunk = (sizeof(buffer) < (unsigned int) nBytes) ?
462            sizeof(buffer) : nBytes;
463        nRead = fread(buffer, sizeof(char), chunk, NanoVis::stdin);
464        if (ferror(NanoVis::stdin)) {
465            Tcl_AppendResult(interp, "while reading data stream: ",
466                             Tcl_PosixError(interp), (char*)NULL);
467            return TCL_ERROR;
468        }
469        if (feof(NanoVis::stdin)) {
470            Tcl_AppendResult(interp, "premature EOF while reading data stream",
471                             (char*)NULL);
472            return TCL_ERROR;
473        }
474        buf.append(buffer, nRead);
475        nBytes -= nRead;
476    }
477    if (NanoVis::recfile != NULL) {
478        ssize_t nWritten;
479
480        nWritten = fwrite(buf.bytes(), sizeof(char), buf.size(), 
481                          NanoVis::recfile);
482        assert(nWritten == (ssize_t)buf.size());
483        fflush(NanoVis::recfile);
484    }
485    Rappture::Outcome err;
486    TRACE("Checking header[%.13s]", buf.bytes());
487    if (strncmp (buf.bytes(), "@@RP-ENC:", 9) == 0) {
488        /* There's a header on the buffer, use it to decode the data. */
489        if (!Rappture::encoding::decode(err, buf, RPENC_HDR)) {
490            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
491            return TCL_ERROR;
492        }
493    } else if (Rappture::encoding::isBase64(buf.bytes(), buf.size())) {
494        /* No header, but it's base64 encoded.  It's likely that it's both
495         * base64 encoded and compressed. */
496        if (!Rappture::encoding::decode(err, buf, RPENC_B64 | RPENC_Z)) {
497            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
498            return TCL_ERROR;
499        }
500    }
501    return TCL_OK;
502}
503
504static int
505CameraAngleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
506              Tcl_Obj *const *objv)
507{
508    float theta, phi, psi;
509    if ((GetFloatFromObj(interp, objv[2], &phi) != TCL_OK) ||
510        (GetFloatFromObj(interp, objv[3], &theta) != TCL_OK) ||
511        (GetFloatFromObj(interp, objv[4], &psi) != TCL_OK)) {
512        return TCL_ERROR;
513    }
514    NanoVis::cam->rotate(phi, theta, psi);
515    return TCL_OK;
516}
517
518static int
519CameraOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
520               Tcl_Obj *const *objv)
521{
522    double quat[4];
523    if ((Tcl_GetDoubleFromObj(interp, objv[2], &quat[3]) != TCL_OK) ||
524        (Tcl_GetDoubleFromObj(interp, objv[3], &quat[0]) != TCL_OK) ||
525        (Tcl_GetDoubleFromObj(interp, objv[4], &quat[1]) != TCL_OK) ||
526        (Tcl_GetDoubleFromObj(interp, objv[5], &quat[2]) != TCL_OK)) {
527        return TCL_ERROR;
528    }
529    NanoVis::cam->rotate(quat);
530    return TCL_OK;
531}
532
533static int
534CameraPanOp(ClientData clientData, Tcl_Interp *interp, int objc, 
535             Tcl_Obj *const *objv)
536{
537    float x, y;
538    if ((GetFloatFromObj(interp, objv[2], &x) != TCL_OK) ||
539        (GetFloatFromObj(interp, objv[3], &y) != TCL_OK)) {
540        return TCL_ERROR;
541    }
542    NanoVis::pan(x, y);
543    return TCL_OK;
544}
545
546static int
547CameraPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
548                 Tcl_Obj *const *objv)
549{
550    float x, y, z;
551    if ((GetFloatFromObj(interp, objv[2], &x) != TCL_OK) ||
552        (GetFloatFromObj(interp, objv[3], &y) != TCL_OK) ||
553        (GetFloatFromObj(interp, objv[4], &z) != TCL_OK)) {
554        return TCL_ERROR;
555    }
556    NanoVis::cam->x(x);
557    NanoVis::cam->y(y);
558    NanoVis::cam->z(z);
559    return TCL_OK;
560}
561
562static int
563CameraResetOp(ClientData clientData, Tcl_Interp *interp, int objc, 
564              Tcl_Obj *const *objv)
565{
566    bool all = false;
567    if (objc == 3) {
568        const char *string = Tcl_GetString(objv[2]);
569        char c = string[0];
570        if ((c != 'a') || (strcmp(string, "all") != 0)) {
571            Tcl_AppendResult(interp, "bad camera reset option \"", string,
572                             "\": should be all", (char*)NULL);
573            return TCL_ERROR;
574        }
575        all = true;
576    }
577    NanoVis::resetCamera(all);
578    return TCL_OK;
579}
580
581static int
582CameraZoomOp(ClientData clientData, Tcl_Interp *interp, int objc, 
583             Tcl_Obj *const *objv)
584{
585    float z;
586    if (GetFloatFromObj(interp, objv[2], &z) != TCL_OK) {
587        return TCL_ERROR;
588    }
589    NanoVis::zoom(z);
590    return TCL_OK;
591}
592
593static Rappture::CmdSpec cameraOps[] = {
594    {"angle",   2, CameraAngleOp,    5, 5, "xAngle yAngle zAngle",},
595    {"orient",  1, CameraOrientOp,   6, 6, "qw qx qy qz",},
596    {"pan",     2, CameraPanOp,      4, 4, "x y",},
597    {"pos",     2, CameraPositionOp, 5, 5, "x y z",},
598    {"reset",   1, CameraResetOp,    2, 3, "?all?",},
599    {"zoom",    1, CameraZoomOp,     3, 3, "factor",},
600};
601static int nCameraOps = NumCmdSpecs(cameraOps);
602
603static int
604CameraCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
605          Tcl_Obj *const *objv)
606{
607    Tcl_ObjCmdProc *proc;
608
609    proc = Rappture::GetOpFromObj(interp, nCameraOps, cameraOps,
610                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
611    if (proc == NULL) {
612        return TCL_ERROR;
613    }
614    return (*proc) (clientData, interp, objc, objv);
615}
616
617static int
618SnapshotCmd(ClientData clientData, Tcl_Interp *interp, int objc,
619            Tcl_Obj *const *objv)
620{
621    int w, h;
622
623    w = NanoVis::winWidth, h = NanoVis::winHeight;
624
625    NanoVis::resizeOffscreenBuffer(2048, 2048);
626#ifdef notdef
627    NanoVis::cam->setScreenSize(0, 0, NanoVis::winWidth, NanoVis::winHeight);
628    NanoVis::cam->setScreenSize(30, 90, 2048 - 60, 2048 - 120);
629#endif
630    NanoVis::bindOffscreenBuffer();  //enable offscreen render
631    NanoVis::render();
632    NanoVis::readScreen();
633
634    NanoVis::ppmWrite("nv>image -type print -bytes %d");
635    NanoVis::resizeOffscreenBuffer(w, h);
636
637    return TCL_OK;
638}
639
640static int
641CutplanePositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
642                   Tcl_Obj *const *objv)
643{
644    float relval;
645    if (GetFloatFromObj(interp, objv[2], &relval) != TCL_OK) {
646        return TCL_ERROR;
647    }
648
649    // keep this just inside the volume so it doesn't disappear
650    if (relval < 0.01f) {
651        relval = 0.01f;
652    } else if (relval > 0.99f) {
653        relval = 0.99f;
654    }
655
656    int axis;
657    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
658        return TCL_ERROR;
659    }
660
661    std::vector<Volume *> ivol;
662    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
663        return TCL_ERROR;
664    }
665    std::vector<Volume *>::iterator iter;
666    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
667        (*iter)->moveCutplane(axis, relval);
668    }
669    return TCL_OK;
670}
671
672static int
673CutplaneStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
674                Tcl_Obj *const *objv)
675{
676    bool state;
677    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
678        return TCL_ERROR;
679    }
680
681    int axis;
682    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
683        return TCL_ERROR;
684    }
685
686    std::vector<Volume *> ivol;
687    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
688        return TCL_ERROR;
689    }
690    if (state) {
691        std::vector<Volume *>::iterator iter;
692        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
693            (*iter)->enableCutplane(axis);
694        }
695    } else {
696        std::vector<Volume *>::iterator iter;
697        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
698            (*iter)->disableCutplane(axis);
699        }
700    }
701    return TCL_OK;
702}
703
704static int
705CutplaneVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
706                  Tcl_Obj *const *objv)
707{
708    bool state;
709    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
710        return TCL_ERROR;
711    }
712
713    std::vector<Volume *> ivol;
714    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
715        return TCL_ERROR;
716    }
717    std::vector<Volume *>::iterator iter;
718    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
719        (*iter)->cutplanesVisible(state);
720    }
721    return TCL_OK;
722}
723
724static Rappture::CmdSpec cutplaneOps[] = {
725    {"position", 1, CutplanePositionOp, 4, 0, "relval axis ?indices?",},
726    {"state",    1, CutplaneStateOp,    4, 0, "bool axis ?indices?",},
727    {"visible",  1, CutplaneVisibleOp,  3, 0, "bool ?indices?",},
728};
729static int nCutplaneOps = NumCmdSpecs(cutplaneOps);
730
731/*
732 * ----------------------------------------------------------------------
733 * CLIENT COMMAND:
734 *   cutplane state on|off <axis> ?<volume>...?
735 *   cutplane position <relvalue> <axis> ?<volume>...?
736 *
737 * Clients send these commands to manipulate the cutplanes in one or
738 * more data volumes.  The "state" command turns a cutplane on or
739 * off.  The "position" command changes the position to a relative
740 * value in the range 0-1.  The <axis> can be x, y, or z.  These
741 * options are applied to the volumes represented by one or more
742 * <volume> indices.  If no volumes are specified, then all volumes
743 * are updated.
744 * ----------------------------------------------------------------------
745 */
746static int
747CutplaneCmd(ClientData clientData, Tcl_Interp *interp, int objc,
748            Tcl_Obj *const *objv)
749{
750    Tcl_ObjCmdProc *proc;
751
752    proc = Rappture::GetOpFromObj(interp, nCutplaneOps, cutplaneOps,
753                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
754    if (proc == NULL) {
755        return TCL_ERROR;
756    }
757    return (*proc) (clientData, interp, objc, objv);
758}
759
760/*
761 * ClientInfoCmd --
762 *
763 *      Log initial values to stats file.  The first time this is called
764 *      "render_start" is written into the stats file.  Afterwards, it
765 *      is "render_info".
766 *       
767 *         clientinfo list
768 */
769static int
770ClientInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
771              Tcl_Obj *const *objv)
772{
773    Tcl_DString ds;
774    Tcl_Obj *objPtr, *listObjPtr, **items;
775    int result;
776    int i, numItems, length;
777    char buf[BUFSIZ];
778    const char *string;
779    static int first = 1;
780
781    if (objc != 2) {
782        Tcl_AppendResult(interp, "wrong # of arguments: should be \"", 
783                Tcl_GetString(objv[0]), " list\"", (char *)NULL);
784        return TCL_ERROR;
785    }
786#ifdef KEEPSTATS
787    /* Use the initial client key value pairs as the parts for a generating
788     * a unique file name. */
789    int f = NanoVis::getStatsFile(objv[1]);
790    if (f < 0) {
791        Tcl_AppendResult(interp, "can't open stats file: ", 
792                         Tcl_PosixError(interp), (char *)NULL);
793        return TCL_ERROR;
794    }
795#endif
796    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
797    Tcl_IncrRefCount(listObjPtr);
798    if (first) {
799        first = false;
800        objPtr = Tcl_NewStringObj("render_start", 12);
801        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
802        /* renderer */
803        objPtr = Tcl_NewStringObj("renderer", 8);
804        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
805        objPtr = Tcl_NewStringObj("nanovis", 7);
806        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
807        /* pid */
808        objPtr = Tcl_NewStringObj("pid", 3);
809        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
810        Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj((long)NanoVis::stats.pid));
811        /* host */
812        objPtr = Tcl_NewStringObj("host", 4);
813        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
814        gethostname(buf, BUFSIZ-1);
815        buf[BUFSIZ-1] = '\0';
816        objPtr = Tcl_NewStringObj(buf, -1);
817        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
818    } else {
819        objPtr = Tcl_NewStringObj("render_info", 11);
820        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
821    }
822    Tcl_DStringInit(&ds);
823    /* date */
824    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4));
825    strcpy(buf, ctime(&NanoVis::stats.start.tv_sec));
826    buf[strlen(buf) - 1] = '\0';
827    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1));
828    /* date_secs */
829    Tcl_ListObjAppendElement(interp, listObjPtr, 
830                             Tcl_NewStringObj("date_secs", 9));
831    Tcl_ListObjAppendElement(interp, listObjPtr, 
832                             Tcl_NewLongObj(NanoVis::stats.start.tv_sec));
833    /* Client arguments. */
834    if (Tcl_ListObjGetElements(interp, objv[1], &numItems, &items) != TCL_OK) {
835        return TCL_ERROR;
836    }
837    for (i = 0; i < numItems; i++) {
838        Tcl_ListObjAppendElement(interp, listObjPtr, items[i]);
839    }
840    Tcl_DStringInit(&ds);
841    string = Tcl_GetStringFromObj(listObjPtr, &length);
842    Tcl_DStringAppend(&ds, string, length);
843    Tcl_DStringAppend(&ds, "\n", 1);
844#ifdef KEEPSTATS
845    result = NanoVis::writeToStatsFile(f, Tcl_DStringValue(&ds), 
846                                       Tcl_DStringLength(&ds));
847#else
848    TRACE("clientinfo: %s", Tcl_DStringValue(&ds));
849#endif
850    Tcl_DStringFree(&ds);
851    Tcl_DecrRefCount(listObjPtr);
852    return result;
853}
854
855/*
856 * ----------------------------------------------------------------------
857 * CLIENT COMMAND:
858 *   legend <volumeIndex> <width> <height>
859 *
860 * Clients use this to generate a legend image for the specified
861 * transfer function.  The legend image is a color gradient from 0
862 * to one, drawn in the given transfer function.  The resulting image
863 * is returned in the size <width> x <height>.
864 * ----------------------------------------------------------------------
865 */
866static int
867LegendCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
868          Tcl_Obj *const *objv)
869{
870    if (objc != 4) {
871        Tcl_AppendResult(interp, "wrong # args: should be \"",
872            Tcl_GetString(objv[0]), " transfunc width height\"", (char*)NULL);
873        return TCL_ERROR;
874    }
875
876    const char *tfName = Tcl_GetString(objv[1]);
877    TransferFunction *tf = NanoVis::getTransferFunction(tfName);
878    if (tf == NULL) {
879        Tcl_AppendResult(interp, "unknown transfer function \"", tfName, "\"",
880                             (char*)NULL);
881        return TCL_ERROR;
882    }
883    int w, h;
884    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
885        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
886        return TCL_ERROR;
887    }
888    if (Volume::updatePending) {
889        NanoVis::setVolumeRanges();
890    }
891    NanoVis::renderLegend(tf, Volume::valueMin, Volume::valueMax, w, h, tfName);
892    return TCL_OK;
893}
894
895static int
896ScreenBgColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
897                Tcl_Obj *const *objv)
898{
899    float rgb[3];
900    if ((GetFloatFromObj(interp, objv[2], &rgb[0]) != TCL_OK) ||
901        (GetFloatFromObj(interp, objv[3], &rgb[1]) != TCL_OK) ||
902        (GetFloatFromObj(interp, objv[4], &rgb[2]) != TCL_OK)) {
903        return TCL_ERROR;
904    }
905    NanoVis::setBgColor(rgb);
906    return TCL_OK;
907}
908
909/*
910 * ----------------------------------------------------------------------
911 * CLIENT COMMAND:
912 *   screen size <width> <height>
913 *
914 * Clients send this command to set the size of the rendering area.
915 * Future images are generated at the specified width/height.
916 * ----------------------------------------------------------------------
917 */
918static int
919ScreenSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
920             Tcl_Obj *const *objv)
921{
922    int w, h;
923    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
924        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
925        return TCL_ERROR;
926    }
927    NanoVis::resizeOffscreenBuffer(w, h);
928    return TCL_OK;
929}
930
931static Rappture::CmdSpec screenOps[] = {
932    {"bgcolor",  1, ScreenBgColorOp,  5, 5, "r g b",},
933    {"size",     1, ScreenSizeOp, 4, 4, "width height",},
934};
935static int nScreenOps = NumCmdSpecs(screenOps);
936
937static int
938ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
939          Tcl_Obj *const *objv)
940{
941    Tcl_ObjCmdProc *proc;
942
943    proc = Rappture::GetOpFromObj(interp, nScreenOps, screenOps,
944                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
945    if (proc == NULL) {
946        return TCL_ERROR;
947    }
948    return (*proc) (clientData, interp, objc, objv);
949}
950
951/*
952 * ----------------------------------------------------------------------
953 * CLIENT COMMAND:
954 *   transfunc define <name> <colormap> <alphamap>
955 *     where <colormap> = { <v> <r> <g> <b> ... }
956 *           <alphamap> = { <v> <w> ... }
957 *
958 * Clients send these commands to manipulate the transfer functions.
959 * ----------------------------------------------------------------------
960 */
961static int
962TransfuncCmd(ClientData clientData, Tcl_Interp *interp, int objc,
963             Tcl_Obj *const *objv)
964{
965    if (objc < 2) {
966        Tcl_AppendResult(interp, "wrong # args: should be \"",
967                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
968        return TCL_ERROR;
969    }
970
971    const char *string = Tcl_GetString(objv[1]);
972    char c = string[0];
973    if ((c == 'd') && (strcmp(string, "define") == 0)) {
974        if (objc != 5) {
975            Tcl_AppendResult(interp, "wrong # args: should be \"",
976                Tcl_GetString(objv[0]), " define name colorMap alphaMap\"",
977                (char*)NULL);
978            return TCL_ERROR;
979        }
980
981        // decode the data and store in a series of fields
982        int cmapc, amapc, i;
983        Tcl_Obj **cmapv;
984        Tcl_Obj **amapv;
985
986        amapv = cmapv = NULL;
987        if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
988            return TCL_ERROR;
989        }
990        if ((cmapc % 4) != 0) {
991            Tcl_AppendResult(interp, "wrong # elements is colormap: should be ",
992                "{ v r g b ... }", (char*)NULL);
993            return TCL_ERROR;
994        }
995        if (Tcl_ListObjGetElements(interp, objv[4], &amapc, &amapv) != TCL_OK) {
996            return TCL_ERROR;
997        }
998        if ((amapc % 2) != 0) {
999            Tcl_AppendResult(interp, "wrong # elements in alphamap: should be ",
1000                " { v w ... }", (char*)NULL);
1001            return TCL_ERROR;
1002        }
1003
1004        int numColors = cmapc/4;
1005        float *colorKeys = new float[numColors];
1006        Vector3f *colors = new Vector3f[numColors];
1007        for (i = 0; i < cmapc; i += 4) {
1008            int j;
1009            double q[4];
1010
1011            for (j=0; j < 4; j++) {
1012                if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &q[j]) != TCL_OK) {
1013                    return TCL_ERROR;
1014                }
1015                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1016                    Tcl_AppendResult(interp, "bad colormap value \"",
1017                        Tcl_GetString(cmapv[i+j]),
1018                        "\": should be in the range 0-1", (char*)NULL);
1019                    return TCL_ERROR;
1020                }
1021            }
1022
1023            colorKeys[i/4] = (float)q[0];
1024            colors[i/4].set((float)q[1], (float)q[2], (float)q[3]);
1025        }
1026        int numAlphas = amapc/2;
1027        float *alphaKeys = new float[numAlphas];
1028        float *alphas = new float[numAlphas];
1029        for (i=0; i < amapc; i += 2) {
1030            double q[2];
1031            int j;
1032
1033            for (j=0; j < 2; j++) {
1034                if (Tcl_GetDoubleFromObj(interp, amapv[i+j], &q[j]) != TCL_OK) {
1035                    return TCL_ERROR;
1036                }
1037                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1038                    Tcl_AppendResult(interp, "bad alphamap value \"",
1039                        Tcl_GetString(amapv[i+j]),
1040                        "\": should be in the range 0-1", (char*)NULL);
1041                    return TCL_ERROR;
1042                }
1043            }
1044            alphaKeys[i/2] = (float)q[0];
1045            alphas[i/2] = (float)q[1];
1046        }
1047        // sample the given function into discrete slots
1048        const int nslots = 256;
1049        float data[4*nslots];
1050        for (i=0; i < nslots; i++) {
1051            float x = float(i)/(nslots-1);
1052            Vector3f color;
1053            float alpha;
1054            TransferFunction::sample(x, colorKeys, colors, numColors, &color);
1055            TransferFunction::sample(x, alphaKeys, alphas, numAlphas, &alpha);
1056
1057            data[4*i]   = color.r;
1058            data[4*i+1] = color.g;
1059            data[4*i+2] = color.b;
1060            data[4*i+3] = alpha;
1061        }
1062        delete [] colorKeys;
1063        delete [] colors;
1064        delete [] alphaKeys;
1065        delete [] alphas;
1066        // find or create this transfer function
1067        NanoVis::defineTransferFunction(Tcl_GetString(objv[2]), nslots, data);
1068    } else {
1069        Tcl_AppendResult(interp, "bad option \"", string,
1070                "\": should be define", (char*)NULL);
1071        return TCL_ERROR;
1072    }
1073    return TCL_OK;
1074}
1075
1076/*
1077 * ----------------------------------------------------------------------
1078 * CLIENT COMMAND:
1079 *   up axis
1080 *
1081 * Clients use this to set the "up" direction for all volumes.  Volumes
1082 * are oriented such that this direction points upward.
1083 * ----------------------------------------------------------------------
1084 */
1085static int
1086UpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1087{
1088    if (objc != 2) {
1089        Tcl_AppendResult(interp, "wrong # args: should be \"",
1090                         Tcl_GetString(objv[0]), " x|y|z|-x|-y|-z\"", (char*)NULL);
1091        return TCL_ERROR;
1092    }
1093
1094    int sign;
1095    int axis;
1096    if (GetAxisDirFromObj(interp, objv[1], &axis, &sign) != TCL_OK) {
1097        return TCL_ERROR;
1098    }
1099    NanoVis::updir = (axis+1)*sign;
1100    return TCL_OK;
1101}
1102
1103static int
1104VolumeAnimationCaptureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1105                         Tcl_Obj *const *objv)
1106{
1107    int total;
1108    if (Tcl_GetIntFromObj(interp, objv[3], &total) != TCL_OK) {
1109        return TCL_ERROR;
1110    }
1111    VolumeInterpolator *interpolator =
1112        NanoVis::volRenderer->getVolumeInterpolator();
1113    interpolator->start();
1114    if (interpolator->isStarted()) {
1115        const char *dirName = (objc < 5) ? NULL : Tcl_GetString(objv[4]);
1116        for (int frameNum = 0; frameNum < total; ++frameNum) {
1117            float fraction;
1118
1119            fraction = ((float)frameNum) / (total - 1);
1120            TRACE("fraction : %f", fraction);
1121            interpolator->update(fraction);
1122
1123            int fboOrig;
1124            glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboOrig);
1125
1126            NanoVis::bindOffscreenBuffer();  //enable offscreen render
1127
1128            NanoVis::render();
1129            NanoVis::readScreen();
1130
1131            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboOrig);
1132
1133            NanoVis::bmpWriteToFile(frameNum, dirName);
1134        }
1135    }
1136    return TCL_OK;
1137}
1138
1139static int
1140VolumeAnimationClearOp(ClientData clientData, Tcl_Interp *interp, int objc,
1141                       Tcl_Obj *const *objv)
1142{
1143    NanoVis::volRenderer->clearAnimatedVolumeInfo();
1144    return TCL_OK;
1145}
1146
1147static int
1148VolumeAnimationStartOp(ClientData clientData, Tcl_Interp *interp, int objc,
1149                       Tcl_Obj *const *objv)
1150{
1151    NanoVis::volRenderer->startVolumeAnimation();
1152    return TCL_OK;
1153}
1154
1155static int
1156VolumeAnimationStopOp(ClientData clientData, Tcl_Interp *interp, int objc,
1157                      Tcl_Obj *const *objv)
1158{
1159    NanoVis::volRenderer->stopVolumeAnimation();
1160    return TCL_OK;
1161}
1162
1163static int
1164VolumeAnimationVolumesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1165                         Tcl_Obj *const *objv)
1166{
1167    std::vector<Volume *> volumes;
1168    if (GetVolumes(interp, objc - 3, objv + 3, &volumes) != TCL_OK) {
1169        return TCL_ERROR;
1170    }
1171    TRACE("parsing volume data identifier");
1172    NanoVis::VolumeHashmap::iterator itr;
1173    for (itr = NanoVis::volumeTable.begin();
1174         itr != NanoVis::volumeTable.end(); ++itr) {
1175        NanoVis::volRenderer->addAnimatedVolume(itr->second);
1176    }
1177    return TCL_OK;
1178}
1179
1180static Rappture::CmdSpec volumeAnimationOps[] = {
1181    {"capture",   2, VolumeAnimationCaptureOp,  4, 5, "numframes ?filename?",},
1182    {"clear",     2, VolumeAnimationClearOp,    3, 3, "",},
1183    {"start",     3, VolumeAnimationStartOp,    3, 3, "",},
1184    {"stop",      3, VolumeAnimationStopOp,     3, 3, "",},
1185    {"volumes",   1, VolumeAnimationVolumesOp,  3, 0, "?indices?",},
1186};
1187
1188static int nVolumeAnimationOps = NumCmdSpecs(volumeAnimationOps);
1189
1190static int
1191VolumeAnimationOp(ClientData clientData, Tcl_Interp *interp, int objc,
1192                  Tcl_Obj *const *objv)
1193{
1194    Tcl_ObjCmdProc *proc;
1195
1196    proc = Rappture::GetOpFromObj(interp, nVolumeAnimationOps, 
1197                volumeAnimationOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1198    if (proc == NULL) {
1199        return TCL_ERROR;
1200    }
1201    return (*proc) (clientData, interp, objc, objv);
1202}
1203
1204static int
1205VolumeDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1206                    Tcl_Obj *const *objv)
1207{
1208    TRACE("Data Loading");
1209
1210    int nbytes;
1211    if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
1212        return TCL_ERROR;
1213    }
1214    const char *tag = Tcl_GetString(objv[4]);
1215    Rappture::Buffer buf;
1216    if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
1217        return TCL_ERROR;
1218    }
1219    const char *bytes;
1220    size_t nBytes;
1221
1222    bytes = buf.bytes();
1223    nBytes = buf.size();
1224
1225    TRACE("Checking header[%.20s]", bytes);
1226
1227    Volume *volume = NULL;
1228
1229    if ((nBytes > 5) && (strncmp(bytes, "<HDR>", 5) == 0)) {
1230        TRACE("ZincBlende Stream loading...");
1231         //std::stringstream fdata(std::ios_base::out|std::ios_base::in|std::ios_base::binary);
1232        //fdata.write(buf.bytes(),buf.size());
1233        //vol = NvZincBlendeReconstructor::getInstance()->loadFromStream(fdata);
1234
1235        volume = NvZincBlendeReconstructor::getInstance()->loadFromMemory((void*) buf.bytes());
1236        if (volume == NULL) {
1237            Tcl_AppendResult(interp, "can't get volume instance", (char *)NULL);
1238            return TCL_ERROR;
1239        }
1240        TRACE("finish loading");
1241
1242        Vector3f scale = volume->getPhysicalScaling();
1243        Vector3f loc(scale);
1244        loc *= -0.5;
1245        volume->location(loc);
1246
1247        NanoVis::VolumeHashmap::iterator itr = NanoVis::volumeTable.find(tag);
1248        if (itr != NanoVis::volumeTable.end()) {
1249            Tcl_AppendResult(interp, "volume \"", tag, "\" already exists.",
1250                             (char *)NULL);
1251            return TCL_ERROR;
1252        }
1253        NanoVis::volumeTable[tag] = volume;
1254        volume->name(tag);
1255    } else if ((nBytes > 14) && (strncmp(bytes, "# vtk DataFile", 14) == 0)) {
1256        TRACE("VTK loading...");
1257        std::stringstream fdata;
1258        fdata.write(bytes, nBytes);
1259        if (nBytes <= 0) {
1260            ERROR("data buffer is empty");
1261            abort();
1262        }
1263        Rappture::Outcome context;
1264        volume = load_vtk_volume_stream(context, tag, fdata);
1265        if (volume == NULL) {
1266            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1267            return TCL_ERROR;
1268        }
1269    } else {
1270        // **Deprecated** OpenDX format
1271        if ((nBytes > 5) && (strncmp(bytes, "<ODX>", 5) == 0)) {
1272            bytes += 5;
1273            nBytes -= 5;
1274        } else if ((nBytes > 4) && (strncmp(bytes, "<DX>", 4) == 0)) {
1275            bytes += 4;
1276            nBytes -= 4;
1277        }
1278        TRACE("DX loading...");
1279        std::stringstream fdata;
1280        fdata.write(bytes, nBytes);
1281        if (nBytes <= 0) {
1282            ERROR("data buffer is empty");
1283            abort();
1284        }
1285        Rappture::Outcome context;
1286        volume = load_dx_volume_stream(context, tag, fdata);
1287        if (volume == NULL) {
1288            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1289            return TCL_ERROR;
1290        }
1291    }
1292
1293    if (volume != NULL) {
1294        volume->disableCutplane(0);
1295        volume->disableCutplane(1);
1296        volume->disableCutplane(2);
1297        volume->transferFunction(NanoVis::getTransferFunction("default"));
1298        volume->visible(true);
1299
1300        char info[1024];
1301        ssize_t nWritten;
1302
1303        if (Volume::updatePending) {
1304            NanoVis::setVolumeRanges();
1305        }
1306
1307        // FIXME: strlen(info) is the return value of sprintf
1308        sprintf(info, "nv>data tag %s min %g max %g vmin %g vmax %g\n", tag, 
1309                volume->wAxis.min(), volume->wAxis.max(),
1310                Volume::valueMin, Volume::valueMax);
1311        nWritten  = write(1, info, strlen(info));
1312        assert(nWritten == (ssize_t)strlen(info));
1313    }
1314    return TCL_OK;
1315}
1316
1317static int
1318VolumeDataStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1319                  Tcl_Obj *const *objv)
1320{
1321    bool state;
1322    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1323        return TCL_ERROR;
1324    }
1325    std::vector<Volume *> ivol;
1326    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1327        return TCL_ERROR;
1328    }
1329    std::vector<Volume *>::iterator iter;
1330    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1331        (*iter)->dataEnabled(state);
1332    }
1333    return TCL_OK;
1334}
1335
1336static Rappture::CmdSpec volumeDataOps[] = {
1337    {"follows",   1, VolumeDataFollowsOp, 5, 5, "nbytes tag",},
1338    {"state",     1, VolumeDataStateOp,   4, 0, "bool ?indices?",},
1339};
1340static int nVolumeDataOps = NumCmdSpecs(volumeDataOps);
1341
1342static int
1343VolumeDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1344             Tcl_Obj *const *objv)
1345{
1346    Tcl_ObjCmdProc *proc;
1347
1348    proc = Rappture::GetOpFromObj(interp, nVolumeDataOps, volumeDataOps,
1349                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1350    if (proc == NULL) {
1351        return TCL_ERROR;
1352    }
1353    return (*proc) (clientData, interp, objc, objv);
1354}
1355
1356static int
1357VolumeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1358               Tcl_Obj *const *objv)
1359{
1360    for (int i = 2; i < objc; i++) {
1361        Volume *volume;
1362        if (GetVolumeFromObj(interp, objv[i], &volume) != TCL_OK) {
1363            return TCL_ERROR;
1364        }
1365        NanoVis::removeVolume(volume);
1366    }
1367    NanoVis::eventuallyRedraw();
1368    return TCL_OK;
1369}
1370
1371static int
1372VolumeExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1373               Tcl_Obj *const *objv)
1374{
1375    bool value;
1376    Volume *volume;
1377
1378    value = false;
1379    if (GetVolumeFromObj(NULL, objv[2], &volume) == TCL_OK) {
1380        value = true;
1381    }
1382    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
1383    return TCL_OK;
1384}
1385
1386static int
1387VolumeNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1388              Tcl_Obj *const *objv)
1389{
1390    Tcl_Obj *listObjPtr;
1391    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1392    NanoVis::VolumeHashmap::iterator itr;
1393    for (itr = NanoVis::volumeTable.begin();
1394         itr != NanoVis::volumeTable.end(); ++itr) {
1395        Tcl_Obj *objPtr = Tcl_NewStringObj(itr->second->name(), -1);
1396        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1397    }
1398    Tcl_SetObjResult(interp, listObjPtr);
1399    return TCL_OK;
1400}
1401
1402static int
1403VolumeOutlineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1404                     Tcl_Obj *const *objv)
1405{
1406    float rgb[3];
1407    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1408        return TCL_ERROR;
1409    }
1410    std::vector<Volume *> ivol;
1411    if (GetVolumes(interp, objc - 6, objv + 6, &ivol) != TCL_OK) {
1412        return TCL_ERROR;
1413    }
1414    std::vector<Volume *>::iterator iter;
1415    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1416        (*iter)->setOutlineColor(rgb);
1417    }
1418    return TCL_OK;
1419}
1420
1421static int
1422VolumeOutlineStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1423                     Tcl_Obj *const *objv)
1424{
1425    bool state;
1426    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1427        return TCL_ERROR;
1428    }
1429    std::vector<Volume *> ivol;
1430    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1431        return TCL_ERROR;
1432    }
1433    std::vector<Volume *>::iterator iter;
1434    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1435        (*iter)->outline(state);
1436    }
1437    return TCL_OK;
1438}
1439
1440static Rappture::CmdSpec volumeOutlineOps[] = {
1441    {"color",     1, VolumeOutlineColorOp,  6, 0, "r g b ?indices?",},
1442    {"state",     1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1443    {"visible",   1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1444};
1445static int nVolumeOutlineOps = NumCmdSpecs(volumeOutlineOps);
1446
1447static int
1448VolumeOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc,
1449                Tcl_Obj *const *objv)
1450{
1451    Tcl_ObjCmdProc *proc;
1452
1453    proc = Rappture::GetOpFromObj(interp, nVolumeOutlineOps, volumeOutlineOps,
1454        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1455    if (proc == NULL) {
1456        return TCL_ERROR;
1457    }
1458    return (*proc) (clientData, interp, objc, objv);
1459}
1460
1461static int
1462VolumeShadingAmbientOp(ClientData clientData, Tcl_Interp *interp, int objc,
1463                       Tcl_Obj *const *objv)
1464{
1465    float ambient;
1466    if (GetFloatFromObj(interp, objv[3], &ambient) != TCL_OK) {
1467        return TCL_ERROR;
1468    }
1469    if (ambient < 0.0f || ambient > 1.0f) {
1470        WARN("Invalid ambient coefficient requested: %g, should be [0,1]", ambient);
1471    }
1472    std::vector<Volume *> ivol;
1473    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1474        return TCL_ERROR;
1475    }
1476    std::vector<Volume *>::iterator iter;
1477    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1478        (*iter)->ambient(ambient);
1479    }
1480    return TCL_OK;
1481}
1482
1483static int
1484VolumeShadingDiffuseOp(ClientData clientData, Tcl_Interp *interp, int objc,
1485                       Tcl_Obj *const *objv)
1486{
1487    float diffuse;
1488    if (GetFloatFromObj(interp, objv[3], &diffuse) != TCL_OK) {
1489        return TCL_ERROR;
1490    }
1491    if (diffuse < 0.0f || diffuse > 1.0f) {
1492        WARN("Invalid diffuse coefficient requested: %g, should be [0,1]", diffuse);
1493    }
1494    std::vector<Volume *> ivol;
1495    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1496        return TCL_ERROR;
1497    }
1498    std::vector<Volume *>::iterator iter;
1499    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1500        (*iter)->diffuse(diffuse);
1501    }
1502    return TCL_OK;
1503}
1504
1505static int
1506VolumeShadingIsosurfaceOp(ClientData clientData, Tcl_Interp *interp, int objc,
1507                          Tcl_Obj *const *objv)
1508{
1509    bool iso_surface;
1510    if (GetBooleanFromObj(interp, objv[3], &iso_surface) != TCL_OK) {
1511        return TCL_ERROR;
1512    }
1513    std::vector<Volume *> ivol;
1514    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1515        return TCL_ERROR;
1516    }
1517    std::vector<Volume *>::iterator iter;
1518    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1519        (*iter)->isosurface(iso_surface);
1520    }
1521    return TCL_OK;
1522}
1523
1524static int
1525VolumeShadingLight2SideOp(ClientData clientData, Tcl_Interp *interp, int objc,
1526                          Tcl_Obj *const *objv)
1527{
1528    bool twoSidedLighting;
1529    if (GetBooleanFromObj(interp, objv[3], &twoSidedLighting) != TCL_OK) {
1530        return TCL_ERROR;
1531    }
1532    std::vector<Volume *> ivol;
1533    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1534        return TCL_ERROR;
1535    }
1536    std::vector<Volume *>::iterator iter;
1537    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1538        (*iter)->twoSidedLighting(twoSidedLighting);
1539    }
1540    return TCL_OK;
1541}
1542
1543static int
1544VolumeShadingOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1545                       Tcl_Obj *const *objv)
1546{
1547
1548    float opacity;
1549    if (GetFloatFromObj(interp, objv[3], &opacity) != TCL_OK) {
1550        return TCL_ERROR;
1551    }
1552    TRACE("set opacity %f", opacity);
1553    std::vector<Volume *> ivol;
1554    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1555        return TCL_ERROR;
1556    }
1557    std::vector<Volume *>::iterator iter;
1558    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1559        (*iter)->opacityScale(opacity);
1560    }
1561    return TCL_OK;
1562}
1563
1564static int
1565VolumeShadingSpecularOp(ClientData clientData, Tcl_Interp *interp, int objc,
1566                        Tcl_Obj *const *objv)
1567{
1568    float specular;
1569    if (GetFloatFromObj(interp, objv[3], &specular) != TCL_OK) {
1570        return TCL_ERROR;
1571    }
1572    if (specular < 0.0f || specular > 1.0f) {
1573        WARN("Invalid specular coefficient requested: %g, should be [0,1]", specular);
1574    }
1575    std::vector<Volume *> ivol;
1576    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1577        return TCL_ERROR;
1578    }
1579    std::vector<Volume *>::iterator iter;
1580    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1581        (*iter)->specularLevel(specular);
1582    }
1583    return TCL_OK;
1584}
1585
1586static int
1587VolumeShadingSpecularExpOp(ClientData clientData, Tcl_Interp *interp, int objc,
1588                           Tcl_Obj *const *objv)
1589{
1590    float specularExp;
1591    if (GetFloatFromObj(interp, objv[3], &specularExp) != TCL_OK) {
1592        return TCL_ERROR;
1593    }
1594    if (specularExp < 0.0f || specularExp > 128.0f) {
1595        WARN("Invalid specular exponent requested: %g, should be [0,128]", specularExp);
1596    }
1597    std::vector<Volume *> ivol;
1598    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1599        return TCL_ERROR;
1600    }
1601    std::vector<Volume *>::iterator iter;
1602    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1603        (*iter)->specularExponent(specularExp);
1604    }
1605    return TCL_OK;
1606}
1607
1608static int
1609VolumeShadingTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1610                         Tcl_Obj *const *objv)
1611{
1612    const char *name = Tcl_GetString(objv[3]);
1613    TransferFunction *tf = NanoVis::getTransferFunction(name);
1614    if (tf == NULL) {
1615        Tcl_AppendResult(interp, "transfer function \"", name,
1616                         "\" is not defined", (char*)NULL);
1617        return TCL_ERROR;
1618    }
1619    std::vector<Volume *> ivol;
1620    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1621        return TCL_ERROR;
1622    }
1623    std::vector<Volume *>::iterator iter;
1624    for (iter = ivol.begin(); iter != ivol.end(); ++iter) {
1625        TRACE("setting %s with transfer function %s", (*iter)->name(),
1626               tf->name());
1627        (*iter)->transferFunction(tf);
1628#ifdef USE_POINTSET_RENDERER
1629        // TBD..
1630        if ((*iter)->pointsetIndex != -1) {
1631            NanoVis::pointSet[(*iter)->pointsetIndex]->updateColor(tf->getData(), 256);
1632        }
1633#endif
1634    }
1635    return TCL_OK;
1636}
1637
1638static Rappture::CmdSpec volumeShadingOps[] = {
1639    {"ambient",       1, VolumeShadingAmbientOp,     4, 0, "value ?indices?",},
1640    {"diffuse",       1, VolumeShadingDiffuseOp,     4, 0, "value ?indices?",},
1641    {"isosurface",    1, VolumeShadingIsosurfaceOp,  4, 0, "bool ?indices?",},
1642    {"light2side",    1, VolumeShadingLight2SideOp,  4, 0, "bool ?indices?",},
1643    {"opacity",       1, VolumeShadingOpacityOp,     4, 0, "value ?indices?",},
1644    {"specularExp",   9, VolumeShadingSpecularExpOp, 4, 0, "value ?indices?",},
1645    {"specularLevel", 9, VolumeShadingSpecularOp,    4, 0, "value ?indices?",},
1646    {"transfunc",     1, VolumeShadingTransFuncOp,   4, 0, "funcName ?indices?",},
1647};
1648static int nVolumeShadingOps = NumCmdSpecs(volumeShadingOps);
1649
1650static int
1651VolumeShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1652                Tcl_Obj *const *objv)
1653{
1654    Tcl_ObjCmdProc *proc;
1655
1656    proc = Rappture::GetOpFromObj(interp, nVolumeShadingOps, volumeShadingOps,
1657        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1658    if (proc == NULL) {
1659        return TCL_ERROR;
1660    }
1661    return (*proc) (clientData, interp, objc, objv);
1662}
1663
1664static int
1665VolumeStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1666              Tcl_Obj *const *objv)
1667{
1668    bool state;
1669    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1670        return TCL_ERROR;
1671    }
1672    std::vector<Volume *> ivol;
1673    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
1674        return TCL_ERROR;
1675    }
1676    std::vector<Volume *>::iterator iter;
1677    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1678        (*iter)->visible(state);
1679    }
1680    return TCL_OK;
1681}
1682
1683static Rappture::CmdSpec volumeOps[] = {
1684    {"animation", 2, VolumeAnimationOp,   3, 0, "oper ?args?",},
1685    {"data",      2, VolumeDataOp,        3, 0, "oper ?args?",},
1686    {"delete",    2, VolumeDeleteOp,      3, 0, "?name...?",},
1687    {"exists",    1, VolumeExistsOp,      3, 3, "name",},
1688    {"names",     1, VolumeNamesOp,       2, 2, "",},
1689    {"outline",   1, VolumeOutlineOp,     3, 0, "oper ?args?",},
1690    {"shading",   2, VolumeShadingOp,     3, 0, "oper ?args?",},
1691    {"state",     2, VolumeStateOp,       3, 0, "bool ?indices?",},
1692};
1693static int nVolumeOps = NumCmdSpecs(volumeOps);
1694
1695static int
1696VolumeCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
1697          Tcl_Obj *const *objv)
1698{
1699    Tcl_ObjCmdProc *proc;
1700
1701    proc = Rappture::GetOpFromObj(interp, nVolumeOps, volumeOps,
1702        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1703    if (proc == NULL) {
1704        return TCL_ERROR;
1705    }
1706    return (*proc) (clientData, interp, objc, objv);
1707}
1708
1709static int
1710HeightMapDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1711                       Tcl_Obj *const *objv)
1712{
1713    int nBytes;
1714    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
1715        return TCL_ERROR;
1716    }
1717    const char *tag = Tcl_GetString(objv[4]);
1718
1719    Rappture::Buffer buf;
1720    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
1721        return TCL_ERROR;
1722    }
1723    Rappture::Unirect2d data(1);
1724    if (data.parseBuffer(interp, buf) != TCL_OK) {
1725        return TCL_ERROR;
1726    }
1727    if (data.nValues() == 0) {
1728        Tcl_AppendResult(interp, "no data found in stream", (char *)NULL);
1729        return TCL_ERROR;
1730    }
1731    if (!data.isInitialized()) {
1732        return TCL_ERROR;
1733    }
1734    HeightMap *heightMap;
1735    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(tag);
1736    if (itr != NanoVis::heightMapTable.end()) {
1737        heightMap = itr->second;
1738    } else {
1739        heightMap = new HeightMap();
1740        NanoVis::heightMapTable[tag] = heightMap;
1741    }
1742    TRACE("Number of heightmaps=%d", NanoVis::heightMapTable.size());
1743    // Must set units before the heights.
1744    heightMap->xAxis.units(data.xUnits());
1745    heightMap->yAxis.units(data.yUnits());
1746    heightMap->zAxis.units(data.vUnits());
1747    heightMap->wAxis.units(data.yUnits());
1748    heightMap->setHeight(data.xMin(), data.yMin(), data.xMax(), data.yMax(), 
1749                         data.xNum(), data.yNum(), data.transferValues());
1750    heightMap->transferFunction(NanoVis::getTransferFunction("default"));
1751    heightMap->setVisible(true);
1752    heightMap->setLineContourVisible(true);
1753    NanoVis::eventuallyRedraw();
1754    return TCL_OK;
1755}
1756
1757static int
1758HeightMapDataVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1759                       Tcl_Obj *const *objv)
1760{
1761    bool visible;
1762    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1763        return TCL_ERROR;
1764    }
1765    std::vector<HeightMap *> imap;
1766    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1767        return TCL_ERROR;
1768    }
1769    std::vector<HeightMap *>::iterator iter;
1770    for (iter = imap.begin(); iter != imap.end(); iter++) {
1771        (*iter)->setVisible(visible);
1772    }
1773    NanoVis::eventuallyRedraw();
1774    return TCL_OK;
1775}
1776
1777static Rappture::CmdSpec heightMapDataOps[] = {
1778    {"follows",  1, HeightMapDataFollowsOp, 5, 5, "size heightmapName",},
1779    {"visible",  1, HeightMapDataVisibleOp, 4, 0, "bool ?heightmapNames...?",},
1780};
1781static int nHeightMapDataOps = NumCmdSpecs(heightMapDataOps);
1782
1783static int
1784HeightMapDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1785                Tcl_Obj *const *objv)
1786{
1787    Tcl_ObjCmdProc *proc;
1788
1789    proc = Rappture::GetOpFromObj(interp, nHeightMapDataOps, heightMapDataOps,
1790                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1791    if (proc == NULL) {
1792        return TCL_ERROR;
1793    }
1794    return (*proc) (clientData, interp, objc, objv);
1795}
1796
1797static int
1798HeightMapLineContourColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1799                            Tcl_Obj *const *objv)
1800{
1801    float rgb[3];
1802    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1803        return TCL_ERROR;
1804    }
1805    std::vector<HeightMap *> imap;
1806    if (GetHeightMaps(interp, objc - 6, objv + 6, &imap) != TCL_OK) {
1807        return TCL_ERROR;
1808    }
1809    std::vector<HeightMap *>::iterator iter;
1810    for (iter = imap.begin(); iter != imap.end(); iter++) {
1811        (*iter)->setLineContourColor(rgb);
1812    }
1813    NanoVis::eventuallyRedraw();
1814    return TCL_OK;
1815}
1816
1817static int
1818HeightMapLineContourVisibleOp(ClientData clientData, Tcl_Interp *interp, 
1819                              int objc, Tcl_Obj *const *objv)
1820{
1821    bool visible;
1822    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1823        return TCL_ERROR;
1824    }
1825    std::vector<HeightMap *> imap;
1826    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1827        return TCL_ERROR;
1828    }
1829    std::vector<HeightMap *>::iterator iter;
1830    for (iter = imap.begin(); iter != imap.end(); iter++) {
1831        (*iter)->setLineContourVisible(visible);
1832    }
1833    NanoVis::eventuallyRedraw();
1834    return TCL_OK;
1835}
1836
1837static Rappture::CmdSpec heightMapLineContourOps[] = {
1838    {"color",   1, HeightMapLineContourColorOp,   6, 0, "r g b ?heightmapNames...?",},
1839    {"visible", 1, HeightMapLineContourVisibleOp, 4, 0, "bool ?heightmapNames...?",},
1840};
1841static int nHeightMapLineContourOps = NumCmdSpecs(heightMapLineContourOps);
1842
1843static int 
1844HeightMapLineContourOp(ClientData clientData, Tcl_Interp *interp, int objc,
1845                       Tcl_Obj *const *objv)
1846{
1847    Tcl_ObjCmdProc *proc;
1848
1849    proc = Rappture::GetOpFromObj(interp, nHeightMapLineContourOps,
1850        heightMapLineContourOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1851    if (proc == NULL) {
1852        return TCL_ERROR;
1853    }
1854    return (*proc) (clientData, interp, objc, objv);
1855}
1856
1857static int
1858HeightMapCullOp(ClientData clientData, Tcl_Interp *interp, int objc,
1859                Tcl_Obj *const *objv)
1860{
1861    RenderContext::CullMode mode;
1862    if (GetCullMode(interp, objv[2], &mode) != TCL_OK) {
1863        return TCL_ERROR;
1864    }
1865    NanoVis::renderContext->setCullMode(mode);
1866    NanoVis::eventuallyRedraw();
1867    return TCL_OK;
1868}
1869
1870static int
1871HeightMapCreateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1872                  Tcl_Obj *const *objv)
1873{
1874    const char *tag = Tcl_GetString(objv[2]);
1875    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(tag);
1876    if (itr != NanoVis::heightMapTable.end()) {
1877        Tcl_AppendResult(interp, "heightmap \"", tag, "\" already exists.",
1878                         (char *)NULL);
1879        return TCL_ERROR;
1880    }
1881    /* heightmap create xmin ymin xmax ymax xnum ynum values */
1882    HeightMap *heightMap = CreateHeightMap(clientData, interp, objc - 3, objv + 3);
1883    if (heightMap == NULL) {
1884        return TCL_ERROR;
1885    }
1886    NanoVis::heightMapTable[tag] = heightMap;
1887    NanoVis::eventuallyRedraw();
1888    TRACE("Number of heightmaps=%d", NanoVis::heightMapTable.size());
1889    return TCL_OK;
1890}
1891
1892static int
1893HeightMapLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
1894                  Tcl_Obj *const *objv)
1895{
1896    HeightMap *hmPtr;
1897    if (GetHeightMapFromObj(interp, objv[2], &hmPtr) != TCL_OK) {
1898        return TCL_ERROR;
1899    }
1900    const char *tag;
1901    tag = Tcl_GetString(objv[2]);
1902    TransferFunction *tfPtr;
1903    tfPtr = hmPtr->transferFunction();
1904    if (tfPtr == NULL) {
1905        Tcl_AppendResult(interp, "no transfer function defined for heightmap"
1906                         " \"", tag, "\"", (char*)NULL);
1907        return TCL_ERROR;
1908    }
1909    int w, h;
1910    if ((Tcl_GetIntFromObj(interp, objv[3], &w) != TCL_OK) ||
1911        (Tcl_GetIntFromObj(interp, objv[4], &h) != TCL_OK)) {
1912        return TCL_ERROR;
1913    }
1914    if (HeightMap::updatePending) {
1915        NanoVis::setHeightmapRanges();
1916    }
1917    NanoVis::renderLegend(tfPtr, HeightMap::valueMin, HeightMap::valueMax, 
1918                          w, h, tag);
1919    return TCL_OK;
1920}
1921
1922static int
1923HeightMapPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
1924                   Tcl_Obj *const *objv)
1925{
1926    RenderContext::PolygonMode mode;
1927    if (GetPolygonMode(interp, objv[2], &mode) != TCL_OK) {
1928        return TCL_ERROR;
1929    }
1930    NanoVis::renderContext->setPolygonMode(mode);
1931    NanoVis::eventuallyRedraw();
1932    return TCL_OK;
1933}
1934
1935static int
1936HeightMapShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1937                 Tcl_Obj *const *objv)
1938{
1939    RenderContext::ShadingModel model;
1940    if (GetShadingModel(interp, objv[2], &model) != TCL_OK) {
1941        return TCL_ERROR;
1942    }
1943    NanoVis::renderContext->setShadingModel(model);
1944    NanoVis::eventuallyRedraw();
1945    return TCL_OK;
1946}
1947
1948static int
1949HeightMapTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1950                     Tcl_Obj *const *objv)
1951{
1952    const char *name;
1953    name = Tcl_GetString(objv[2]);
1954    TransferFunction *tf = NanoVis::getTransferFunction(name);
1955    if (tf == NULL) {
1956        Tcl_AppendResult(interp, "transfer function \"", name,
1957                         "\" is not defined", (char*)NULL);
1958        return TCL_ERROR;
1959    }
1960    std::vector<HeightMap *> imap;
1961    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
1962        return TCL_ERROR;
1963    }
1964    std::vector<HeightMap *>::iterator iter;
1965    for (iter = imap.begin(); iter != imap.end(); iter++) {
1966        (*iter)->transferFunction(tf);
1967    }
1968    NanoVis::eventuallyRedraw();
1969    return TCL_OK;
1970}
1971
1972static int
1973HeightMapOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1974                   Tcl_Obj *const *objv)
1975{
1976    float opacity;
1977    if (GetFloatFromObj(interp, objv[2], &opacity) != TCL_OK) {
1978        return TCL_ERROR;
1979    }
1980    std::vector<HeightMap *> heightmaps;
1981    if (GetHeightMaps(interp, objc - 3, objv + 3, &heightmaps) != TCL_OK) {
1982        return TCL_ERROR;
1983    }
1984    std::vector<HeightMap *>::iterator iter;
1985    for (iter = heightmaps.begin(); iter != heightmaps.end(); iter++) {
1986        (*iter)->opacity(opacity);
1987    }
1988    NanoVis::eventuallyRedraw();
1989    return TCL_OK;
1990}
1991
1992static Rappture::CmdSpec heightMapOps[] = {
1993    {"create",       2, HeightMapCreateOp,      10, 10, "heightmapName xmin ymin xmax ymax xnum ynum values",},
1994    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
1995    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
1996    {"legend",       2, HeightMapLegendOp,      5, 5, "heightmapName width height",},
1997    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
1998    {"opacity",      1, HeightMapOpacityOp,     3, 0, "value ?heightmapNames...? ",},
1999    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
2000    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
2001    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?heightmapNames...?",},
2002};
2003static int nHeightMapOps = NumCmdSpecs(heightMapOps);
2004
2005static int
2006HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2007             Tcl_Obj *const *objv)
2008{
2009    Tcl_ObjCmdProc *proc;
2010
2011    proc = Rappture::GetOpFromObj(interp, nHeightMapOps, heightMapOps,
2012                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2013    if (proc == NULL) {
2014        return TCL_ERROR;
2015    }
2016    return (*proc) (clientData, interp, objc, objv);
2017}
2018
2019static int
2020GridAxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2021                Tcl_Obj *const *objv)
2022{
2023    float r, g, b, a;
2024    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2025        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2026        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2027        return TCL_ERROR;
2028    }
2029    a = 1.0f;
2030    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2031        return TCL_ERROR;
2032    }
2033    if (NanoVis::grid) {
2034        NanoVis::grid->setAxisColor(r, g, b, a);
2035    }
2036    return TCL_OK;
2037}
2038
2039static int
2040GridAxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2041               Tcl_Obj *const *objv)
2042{
2043    int axis;
2044    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
2045        return TCL_ERROR;
2046    }
2047    if (NanoVis::grid != NULL) {
2048        Axis *axisPtr;
2049
2050        axisPtr = NULL;     /* Suppress compiler warning. */
2051        switch (axis) {
2052        case 0: axisPtr = &NanoVis::grid->xAxis; break;
2053        case 1: axisPtr = &NanoVis::grid->yAxis; break;
2054        case 2: axisPtr = &NanoVis::grid->zAxis; break;
2055        }
2056        axisPtr->name(Tcl_GetString(objv[3]));
2057        axisPtr->units(Tcl_GetString(objv[4]));
2058    }
2059    return TCL_OK;
2060}
2061
2062static int
2063GridLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2064                Tcl_Obj *const *objv)
2065{
2066    float r, g, b, a;
2067    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2068        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2069        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2070        return TCL_ERROR;
2071    }
2072    a = 1.0f;
2073    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2074        return TCL_ERROR;
2075    }
2076    if (NanoVis::grid) {
2077        NanoVis::grid->setLineColor(r, g, b, a);
2078    }
2079    return TCL_OK;
2080}
2081
2082static int
2083GridVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2084              Tcl_Obj *const *objv)
2085{
2086    bool visible;
2087    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2088        return TCL_ERROR;
2089    }
2090    NanoVis::grid->setVisible(visible);
2091    return TCL_OK;
2092}
2093
2094static Rappture::CmdSpec gridOps[] = {
2095    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2096    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2097    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2098    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2099};
2100static int nGridOps = NumCmdSpecs(gridOps);
2101
2102static int
2103GridCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2104        Tcl_Obj *const *objv)
2105{
2106    Tcl_ObjCmdProc *proc;
2107
2108    proc = Rappture::GetOpFromObj(interp, nGridOps, gridOps,
2109                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2110    if (proc == NULL) {
2111        return TCL_ERROR;
2112    }
2113    return (*proc) (clientData, interp, objc, objv);
2114}
2115
2116static int
2117AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2118        Tcl_Obj *const *objv)
2119{
2120    if (objc < 2) {
2121        Tcl_AppendResult(interp, "wrong # args: should be \"",
2122                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2123        return TCL_ERROR;
2124    }
2125    const char *string = Tcl_GetString(objv[1]);
2126    char c = string[0];
2127    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2128        bool visible;
2129
2130        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2131            return TCL_ERROR;
2132        }
2133        NanoVis::axisOn = visible;
2134    } else {
2135        Tcl_AppendResult(interp, "bad axis option \"", string,
2136                         "\": should be visible", (char*)NULL);
2137        return TCL_ERROR;
2138    }
2139    return TCL_OK;
2140}
2141
2142/*
2143 * This command should be Tcl procedure instead of a C command.  The reason
2144 * for this that 1) we are using a safe interpreter so we would need a master
2145 * interpreter to load the Tcl environment properly (including our "unirect2d"
2146 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2147 * easy to add new directories for procedures, since it's loaded into /tmp.
2148 *
2149 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2150 * to verify the structure and then pass it to the appropiate Tcl command
2151 * (heightmap, volume, etc). Our C command always creates a heightmap.
2152 */
2153static int
2154Unirect2dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2155             Tcl_Obj *const *objv)
2156{
2157    Rappture::Unirect2d *dataPtr = (Rappture::Unirect2d *)clientData;
2158
2159    return dataPtr->loadData(interp, objc, objv);
2160}
2161
2162/*
2163 * This command should be Tcl procedure instead of a C command.  The reason
2164 * for this that 1) we are using a safe interpreter so we would need a master
2165 * interpreter to load the Tcl environment properly (including our "unirect2d"
2166 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2167 * easy to add new directories for procedures, since it's loaded into /tmp.
2168 *
2169 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2170 * to verify the structure and then pass it to the appropiate Tcl command
2171 * (heightmap, volume, etc). Our C command always creates a heightmap.
2172 */
2173
2174static int
2175Unirect3dCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2176             Tcl_Obj *const *objv)
2177{
2178    Rappture::Unirect3d *dataPtr = (Rappture::Unirect3d *)clientData;
2179
2180    return dataPtr->loadData(interp, objc, objv);
2181}
2182
2183Tcl_Interp *
2184initTcl()
2185{
2186    /*
2187     * Ideally the connection is authenticated by nanoscale.  I still like the
2188     * idea of creating a non-safe master interpreter with a safe slave
2189     * interpreter.  Alias all the nanovis commands in the slave. That way we
2190     * can still run Tcl code within nanovis.  The eventual goal is to create
2191     * a test harness through the interpreter for nanovis.
2192     */
2193    Tcl_Interp *interp;
2194    interp = Tcl_CreateInterp();
2195    /*
2196    Tcl_MakeSafe(interp);
2197    */
2198    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
2199    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
2200    Tcl_CreateObjCommand(interp, "clientinfo",  ClientInfoCmd,  NULL, NULL);
2201    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
2202    if (FlowCmdInitProc(interp) != TCL_OK) {
2203        return NULL;
2204    }
2205    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
2206    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
2207    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
2208    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
2209    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    NULL, NULL);
2210    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   NULL, NULL);
2211    Tcl_CreateObjCommand(interp, "unirect2d",   Unirect2dCmd,   NULL, NULL);
2212    Tcl_CreateObjCommand(interp, "unirect3d",   Unirect3dCmd,   NULL, NULL);
2213    Tcl_CreateObjCommand(interp, "up",          UpCmd,          NULL, NULL);
2214    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
2215
2216    // create a default transfer function
2217    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2218        WARN("bad default transfer function:\n%s", 
2219             Tcl_GetStringResult(interp));
2220    }
2221    return interp;
2222}
Note: See TracBrowser for help on using the repository browser.