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

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

remove dead code

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