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

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

Merge in 'cutplane visible' command

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