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

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

fix comments

  • 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#include "Trace.h"
51#ifdef USE_POINTSET_RENDERER
52#include "PointSet.h"
53#endif
54#include "dxReader.h"
55#include "VtkReader.h"
56#include "Grid.h"
57#include "HeightMap.h"
58#include "NvCamera.h"
59#include "NvZincBlendeReconstructor.h"
60#include "Unirect.h"
61#include "Volume.h"
62#include "VolumeRenderer.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/**
270 * Used internally to decode a series of volume index values and
271 * store then in the specified vector.  If there are no volume index
272 * arguments, this means "all volumes" to most commands, so all
273 * active volume indices are stored in the vector.
274 *
275 * Updates pushes index values into the vector.  Returns TCL_OK or
276 * TCL_ERROR to indicate an error.
277 */
278static int
279GetVolumeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Volume **volPtrPtr)
280{
281    const char *string = Tcl_GetString(objPtr);
282
283    NanoVis::VolumeHashmap::iterator itr = NanoVis::volumeTable.find(string);
284    if (itr == NanoVis::volumeTable.end()) {
285        if (interp != NULL) {
286            Tcl_AppendResult(interp, "can't find a volume named \"",
287                         string, "\"", (char*)NULL);
288        }
289        return TCL_ERROR;
290    }
291    *volPtrPtr = itr->second;
292    return TCL_OK;
293}
294
295/**
296 * Used internally to decode a series of volume index values and
297 * store then in the specified vector.  If there are no volume index
298 * arguments, this means "all volumes" to most commands, so all
299 * active volume indices are stored in the vector.
300 *
301 * Updates pushes index values into the vector.  Returns TCL_OK or
302 * TCL_ERROR to indicate an error.
303 */
304static int
305GetVolumes(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
306           std::vector<Volume *>* vectorPtr)
307{
308    if (objc == 0) {
309        // No arguments. Get all volumes.
310        NanoVis::VolumeHashmap::iterator itr;
311        for (itr = NanoVis::volumeTable.begin();
312             itr != NanoVis::volumeTable.end(); ++itr) {
313            vectorPtr->push_back(itr->second);
314        }
315    } else {
316        // Get the volumes associated with the given index arguments.
317        for (int n = 0; n < objc; n++) {
318            Volume *volume;
319            if (GetVolumeFromObj(interp, objv[n], &volume) != TCL_OK) {
320                return TCL_ERROR;
321            }
322            vectorPtr->push_back(volume);
323        }
324    }
325    return TCL_OK;
326}
327
328/**
329 * Used internally to decode a series of volume index values and
330 * store then in the specified vector.  If there are no volume index
331 * arguments, this means "all volumes" to most commands, so all
332 * active volume indices are stored in the vector.
333 *
334 * Updates pushes index values into the vector.  Returns TCL_OK or
335 * TCL_ERROR to indicate an error.
336 */
337static int
338GetHeightMaps(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
339              std::vector<HeightMap *>* vectorPtr)
340{
341    if (objc == 0) {
342        // No arguments. Get all heightmaps.
343        NanoVis::HeightMapHashmap::iterator itr;
344        for (itr = NanoVis::heightMapTable.begin();
345             itr != NanoVis::heightMapTable.end(); ++itr) {
346            vectorPtr->push_back(itr->second);
347        }
348    } else {
349        for (int n = 0; n < objc; n++) {
350            HeightMap *heightMap;
351            if (GetHeightMapFromObj(interp, objv[n], &heightMap) != TCL_OK) {
352                return TCL_ERROR;
353            }
354            vectorPtr->push_back(heightMap);
355        }
356    }
357    return TCL_OK;
358}
359
360/**
361 * Used internally to decode an axis value from a string ("x", "y",
362 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
363 * along with a value in valPtr.  Otherwise, it returns TCL_ERROR
364 * and an error message in the interpreter.
365 */
366static int
367GetAxis(Tcl_Interp *interp, const char *string, int *indexPtr)
368{
369    if (string[1] == '\0') {
370        char c;
371
372        c = tolower((unsigned char)string[0]);
373        if (c == 'x') {
374            *indexPtr = 0;
375            return TCL_OK;
376        } else if (c == 'y') {
377            *indexPtr = 1;
378            return TCL_OK;
379        } else if (c == 'z') {
380            *indexPtr = 2;
381            return TCL_OK;
382        }
383        /*FALLTHRU*/
384    }
385    Tcl_AppendResult(interp, "bad axis \"", string,
386                     "\": should be x, y, or z", (char*)NULL);
387    return TCL_ERROR;
388}
389
390/**
391 * Used internally to decode an axis value from a string ("x", "y",
392 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
393 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
394 * and an error message in the interpreter.
395 */
396int
397GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr)
398{
399    return GetAxis(interp, Tcl_GetString(objPtr), indexPtr);
400}
401
402/**
403 * Used internally to decode an axis value from a string ("x", "y",
404 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
405 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
406 * and an error message in the interpreter.
407 */
408static int
409GetAxisDirFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr, int *dirPtr)
410{
411    const char *string = Tcl_GetString(objPtr);
412
413    int sign = 1;
414    if (*string == '-') {
415        sign = -1;
416        string++;
417    }
418    if (GetAxis(interp, string, indexPtr) != TCL_OK) {
419        return TCL_ERROR;
420    }
421    if (dirPtr != NULL) {
422        *dirPtr = sign;
423    }
424    return TCL_OK;
425}
426
427/**
428 * Used internally to decode a color value from a string ("R G B")
429 * as a list of three numbers 0-1.  Returns TCL_OK if successful,
430 * along with RGB values in rgbPtr.  Otherwise, it returns TCL_ERROR
431 * and an error message in the interpreter.
432 */
433static int
434GetColor(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, float *rgbPtr)
435{
436    if (objc < 3) {
437        Tcl_AppendResult(interp, "missing color values\": ",
438                         "should list of R G B values 0.0 - 1.0", (char*)NULL);
439        return TCL_ERROR;
440    }
441    if ((GetFloatFromObj(interp, objv[0], rgbPtr + 0) != TCL_OK) ||
442        (GetFloatFromObj(interp, objv[1], rgbPtr + 1) != TCL_OK) ||
443        (GetFloatFromObj(interp, objv[2], rgbPtr + 2) != TCL_OK)) {
444        return TCL_ERROR;
445    }
446    return TCL_OK;
447}
448
449/**
450 * Read the requested number of bytes from standard input into the given
451 * buffer.  The buffer is then decompressed and decoded.
452 */
453int
454GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes)
455{
456    char buffer[8096];
457
458    clearerr(NanoVis::stdin);
459    while (nBytes > 0) {
460        unsigned int chunk;
461        int nRead;
462
463        chunk = (sizeof(buffer) < (unsigned int) nBytes) ?
464            sizeof(buffer) : nBytes;
465        nRead = fread(buffer, sizeof(char), chunk, NanoVis::stdin);
466        if (ferror(NanoVis::stdin)) {
467            Tcl_AppendResult(interp, "while reading data stream: ",
468                             Tcl_PosixError(interp), (char*)NULL);
469            return TCL_ERROR;
470        }
471        if (feof(NanoVis::stdin)) {
472            Tcl_AppendResult(interp, "premature EOF while reading data stream",
473                             (char*)NULL);
474            return TCL_ERROR;
475        }
476        buf.append(buffer, nRead);
477        nBytes -= nRead;
478    }
479    if (NanoVis::recfile != NULL) {
480        ssize_t nWritten;
481
482        nWritten = fwrite(buf.bytes(), sizeof(char), buf.size(), 
483                          NanoVis::recfile);
484        assert(nWritten == (ssize_t)buf.size());
485        fflush(NanoVis::recfile);
486    }
487    Rappture::Outcome err;
488    TRACE("Checking header[%.13s]", buf.bytes());
489    if (strncmp (buf.bytes(), "@@RP-ENC:", 9) == 0) {
490        /* There's a header on the buffer, use it to decode the data. */
491        if (!Rappture::encoding::decode(err, buf, RPENC_HDR)) {
492            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
493            return TCL_ERROR;
494        }
495    } else if (Rappture::encoding::isBase64(buf.bytes(), buf.size())) {
496        /* No header, but it's base64 encoded.  It's likely that it's both
497         * base64 encoded and compressed. */
498        if (!Rappture::encoding::decode(err, buf, RPENC_B64 | RPENC_Z)) {
499            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
500            return TCL_ERROR;
501        }
502    }
503    return TCL_OK;
504}
505
506static int
507CameraAngleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
508              Tcl_Obj *const *objv)
509{
510    float theta, phi, psi;
511    if ((GetFloatFromObj(interp, objv[2], &phi) != TCL_OK) ||
512        (GetFloatFromObj(interp, objv[3], &theta) != TCL_OK) ||
513        (GetFloatFromObj(interp, objv[4], &psi) != TCL_OK)) {
514        return TCL_ERROR;
515    }
516    NanoVis::cam->rotate(phi, theta, psi);
517    return TCL_OK;
518}
519
520static int
521CameraOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
522               Tcl_Obj *const *objv)
523{
524    double quat[4];
525    if ((Tcl_GetDoubleFromObj(interp, objv[2], &quat[3]) != TCL_OK) ||
526        (Tcl_GetDoubleFromObj(interp, objv[3], &quat[0]) != TCL_OK) ||
527        (Tcl_GetDoubleFromObj(interp, objv[4], &quat[1]) != TCL_OK) ||
528        (Tcl_GetDoubleFromObj(interp, objv[5], &quat[2]) != TCL_OK)) {
529        return TCL_ERROR;
530    }
531    NanoVis::cam->rotate(quat);
532    return TCL_OK;
533}
534
535static int
536CameraPanOp(ClientData clientData, Tcl_Interp *interp, int objc, 
537             Tcl_Obj *const *objv)
538{
539    float x, y;
540    if ((GetFloatFromObj(interp, objv[2], &x) != TCL_OK) ||
541        (GetFloatFromObj(interp, objv[3], &y) != TCL_OK)) {
542        return TCL_ERROR;
543    }
544    NanoVis::pan(x, y);
545    return TCL_OK;
546}
547
548static int
549CameraPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
550                 Tcl_Obj *const *objv)
551{
552    float x, y, z;
553    if ((GetFloatFromObj(interp, objv[2], &x) != TCL_OK) ||
554        (GetFloatFromObj(interp, objv[3], &y) != TCL_OK) ||
555        (GetFloatFromObj(interp, objv[4], &z) != TCL_OK)) {
556        return TCL_ERROR;
557    }
558    NanoVis::cam->x(x);
559    NanoVis::cam->y(y);
560    NanoVis::cam->z(z);
561    return TCL_OK;
562}
563
564static int
565CameraResetOp(ClientData clientData, Tcl_Interp *interp, int objc, 
566              Tcl_Obj *const *objv)
567{
568    bool all = false;
569    if (objc == 3) {
570        const char *string = Tcl_GetString(objv[2]);
571        char c = string[0];
572        if ((c != 'a') || (strcmp(string, "all") != 0)) {
573            Tcl_AppendResult(interp, "bad camera reset option \"", string,
574                             "\": should be all", (char*)NULL);
575            return TCL_ERROR;
576        }
577        all = true;
578    }
579    NanoVis::resetCamera(all);
580    return TCL_OK;
581}
582
583static int
584CameraZoomOp(ClientData clientData, Tcl_Interp *interp, int objc, 
585             Tcl_Obj *const *objv)
586{
587    float z;
588    if (GetFloatFromObj(interp, objv[2], &z) != TCL_OK) {
589        return TCL_ERROR;
590    }
591    NanoVis::zoom(z);
592    return TCL_OK;
593}
594
595static Rappture::CmdSpec cameraOps[] = {
596    {"angle",   2, CameraAngleOp,    5, 5, "xAngle yAngle zAngle",},
597    {"orient",  1, CameraOrientOp,   6, 6, "qw qx qy qz",},
598    {"pan",     2, CameraPanOp,      4, 4, "x y",},
599    {"pos",     2, CameraPositionOp, 5, 5, "x y z",},
600    {"reset",   1, CameraResetOp,    2, 3, "?all?",},
601    {"zoom",    1, CameraZoomOp,     3, 3, "factor",},
602};
603static int nCameraOps = NumCmdSpecs(cameraOps);
604
605static int
606CameraCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
607          Tcl_Obj *const *objv)
608{
609    Tcl_ObjCmdProc *proc;
610
611    proc = Rappture::GetOpFromObj(interp, nCameraOps, cameraOps,
612                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
613    if (proc == NULL) {
614        return TCL_ERROR;
615    }
616    return (*proc) (clientData, interp, objc, objv);
617}
618
619static int
620SnapshotCmd(ClientData clientData, Tcl_Interp *interp, int objc,
621            Tcl_Obj *const *objv)
622{
623    int w, h;
624
625    w = NanoVis::winWidth, h = NanoVis::winHeight;
626
627    NanoVis::resizeOffscreenBuffer(2048, 2048);
628#ifdef notdef
629    NanoVis::cam->setScreenSize(0, 0, NanoVis::winWidth, NanoVis::winHeight);
630    NanoVis::cam->setScreenSize(30, 90, 2048 - 60, 2048 - 120);
631#endif
632    NanoVis::bindOffscreenBuffer();  //enable offscreen render
633    NanoVis::render();
634    NanoVis::readScreen();
635
636    NanoVis::ppmWrite("nv>image -type print -bytes %d");
637    NanoVis::resizeOffscreenBuffer(w, h);
638
639    return TCL_OK;
640}
641
642static int
643CutplanePositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
644                   Tcl_Obj *const *objv)
645{
646    float relval;
647    if (GetFloatFromObj(interp, objv[2], &relval) != TCL_OK) {
648        return TCL_ERROR;
649    }
650
651    // keep this just inside the volume so it doesn't disappear
652    if (relval < 0.01f) {
653        relval = 0.01f;
654    } else if (relval > 0.99f) {
655        relval = 0.99f;
656    }
657
658    int axis;
659    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
660        return TCL_ERROR;
661    }
662
663    std::vector<Volume *> ivol;
664    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
665        return TCL_ERROR;
666    }
667    std::vector<Volume *>::iterator iter;
668    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
669        (*iter)->moveCutplane(axis, relval);
670    }
671    return TCL_OK;
672}
673
674/*
675 * cutplane state $bool $axis vol,,,
676 */
677static int
678CutplaneStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
679                Tcl_Obj *const *objv)
680{
681    bool state;
682    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
683        return TCL_ERROR;
684    }
685
686    int axis;
687    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
688        return TCL_ERROR;
689    }
690
691    std::vector<Volume *> ivol;
692    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
693        return TCL_ERROR;
694    }
695    if (state) {
696        std::vector<Volume *>::iterator iter;
697        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
698            (*iter)->enableCutplane(axis);
699        }
700    } else {
701        std::vector<Volume *>::iterator iter;
702        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
703            (*iter)->disableCutplane(axis);
704        }
705    }
706    return TCL_OK;
707}
708
709static Rappture::CmdSpec cutplaneOps[] = {
710    {"position", 1, CutplanePositionOp, 4, 0, "relval axis ?indices?",},
711    {"state",    1, CutplaneStateOp,    4, 0, "bool axis ?indices?",},
712};
713static int nCutplaneOps = NumCmdSpecs(cutplaneOps);
714
715/*
716 * ----------------------------------------------------------------------
717 * CLIENT COMMAND:
718 *   cutplane state on|off <axis> ?<volume>...?
719 *   cutplane position <relvalue> <axis> ?<volume>...?
720 *
721 * Clients send these commands to manipulate the cutplanes in one or
722 * more data volumes.  The "state" command turns a cutplane on or
723 * off.  The "position" command changes the position to a relative
724 * value in the range 0-1.  The <axis> can be x, y, or z.  These
725 * options are applied to the volumes represented by one or more
726 * <volume> indices.  If no volumes are specified, then all volumes
727 * are updated.
728 * ----------------------------------------------------------------------
729 */
730static int
731CutplaneCmd(ClientData clientData, Tcl_Interp *interp, int objc,
732            Tcl_Obj *const *objv)
733{
734    Tcl_ObjCmdProc *proc;
735
736    proc = Rappture::GetOpFromObj(interp, nCutplaneOps, cutplaneOps,
737                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
738    if (proc == NULL) {
739        return TCL_ERROR;
740    }
741    return (*proc) (clientData, interp, objc, objv);
742}
743
744/*
745 * ClientInfoCmd --
746 *
747 *      Log initial values to stats file.  The first time this is called
748 *      "render_start" is written into the stats file.  Afterwards, it
749 *      is "render_info".
750 *       
751 *         clientinfo list
752 */
753static int
754ClientInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
755              Tcl_Obj *const *objv)
756{
757    Tcl_DString ds;
758    Tcl_Obj *objPtr, *listObjPtr, **items;
759    int result;
760    int i, numItems, length;
761    char buf[BUFSIZ];
762    const char *string;
763    static int first = 1;
764
765    if (objc != 2) {
766        Tcl_AppendResult(interp, "wrong # of arguments: should be \"", 
767                Tcl_GetString(objv[0]), " list\"", (char *)NULL);
768        return TCL_ERROR;
769    }
770#ifdef KEEPSTATS
771    /* Use the initial client key value pairs as the parts for a generating
772     * a unique file name. */
773    int f = NanoVis::getStatsFile(objv[1]);
774    if (f < 0) {
775        Tcl_AppendResult(interp, "can't open stats file: ", 
776                         Tcl_PosixError(interp), (char *)NULL);
777        return TCL_ERROR;
778    }
779#endif
780    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
781    Tcl_IncrRefCount(listObjPtr);
782    if (first) {
783        first = false;
784        objPtr = Tcl_NewStringObj("render_start", 12);
785        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
786        /* renderer */
787        objPtr = Tcl_NewStringObj("renderer", 8);
788        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
789        objPtr = Tcl_NewStringObj("nanovis", 7);
790        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
791        /* pid */
792        objPtr = Tcl_NewStringObj("pid", 3);
793        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
794        Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj((long)NanoVis::stats.pid));
795        /* host */
796        objPtr = Tcl_NewStringObj("host", 4);
797        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
798        gethostname(buf, BUFSIZ-1);
799        buf[BUFSIZ-1] = '\0';
800        objPtr = Tcl_NewStringObj(buf, -1);
801        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
802    } else {
803        objPtr = Tcl_NewStringObj("render_info", 11);
804        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
805    }
806    Tcl_DStringInit(&ds);
807    /* date */
808    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4));
809    strcpy(buf, ctime(&NanoVis::stats.start.tv_sec));
810    buf[strlen(buf) - 1] = '\0';
811    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1));
812    /* date_secs */
813    Tcl_ListObjAppendElement(interp, listObjPtr, 
814                             Tcl_NewStringObj("date_secs", 9));
815    Tcl_ListObjAppendElement(interp, listObjPtr, 
816                             Tcl_NewLongObj(NanoVis::stats.start.tv_sec));
817    /* Client arguments. */
818    if (Tcl_ListObjGetElements(interp, objv[1], &numItems, &items) != TCL_OK) {
819        return TCL_ERROR;
820    }
821    for (i = 0; i < numItems; i++) {
822        Tcl_ListObjAppendElement(interp, listObjPtr, items[i]);
823    }
824    Tcl_DStringInit(&ds);
825    string = Tcl_GetStringFromObj(listObjPtr, &length);
826    Tcl_DStringAppend(&ds, string, length);
827    Tcl_DStringAppend(&ds, "\n", 1);
828#ifdef KEEPSTATS
829    result = NanoVis::writeToStatsFile(f, Tcl_DStringValue(&ds), 
830                                       Tcl_DStringLength(&ds));
831#else
832    TRACE("clientinfo: %s", Tcl_DStringValue(&ds));
833#endif
834    Tcl_DStringFree(&ds);
835    Tcl_DecrRefCount(listObjPtr);
836    return result;
837}
838
839/*
840 * ----------------------------------------------------------------------
841 * CLIENT COMMAND:
842 *   legend <volumeIndex> <width> <height>
843 *
844 * Clients use this to generate a legend image for the specified
845 * transfer function.  The legend image is a color gradient from 0
846 * to one, drawn in the given transfer function.  The resulting image
847 * is returned in the size <width> x <height>.
848 * ----------------------------------------------------------------------
849 */
850static int
851LegendCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
852          Tcl_Obj *const *objv)
853{
854    if (objc != 4) {
855        Tcl_AppendResult(interp, "wrong # args: should be \"",
856            Tcl_GetString(objv[0]), " transfunc width height\"", (char*)NULL);
857        return TCL_ERROR;
858    }
859
860    const char *name;
861    name = Tcl_GetString(objv[1]);
862    TransferFunction *tf = NanoVis::getTransferFunction(name);
863    if (tf == NULL) {
864        Tcl_AppendResult(interp, "unknown transfer function \"", name, "\"",
865                             (char*)NULL);
866        return TCL_ERROR;
867    }
868    int w, h;
869    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
870        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
871        return TCL_ERROR;
872    }
873    if (Volume::updatePending) {
874        NanoVis::setVolumeRanges();
875    }
876    NanoVis::renderLegend(tf, Volume::valueMin, Volume::valueMax, w, h, name);
877    return TCL_OK;
878}
879
880static int
881ScreenBgColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
882                Tcl_Obj *const *objv)
883{
884    float rgb[3];
885    if ((GetFloatFromObj(interp, objv[2], &rgb[0]) != TCL_OK) ||
886        (GetFloatFromObj(interp, objv[3], &rgb[1]) != TCL_OK) ||
887        (GetFloatFromObj(interp, objv[4], &rgb[2]) != TCL_OK)) {
888        return TCL_ERROR;
889    }
890    NanoVis::setBgColor(rgb);
891    return TCL_OK;
892}
893
894/*
895 * ----------------------------------------------------------------------
896 * CLIENT COMMAND:
897 *   screen size <width> <height>
898 *
899 * Clients send this command to set the size of the rendering area.
900 * Future images are generated at the specified width/height.
901 * ----------------------------------------------------------------------
902 */
903static int
904ScreenSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
905             Tcl_Obj *const *objv)
906{
907    int w, h;
908    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
909        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
910        return TCL_ERROR;
911    }
912    NanoVis::resizeOffscreenBuffer(w, h);
913    return TCL_OK;
914}
915
916static Rappture::CmdSpec screenOps[] = {
917    {"bgcolor",  1, ScreenBgColorOp,  5, 5, "r g b",},
918    {"size",     1, ScreenSizeOp, 4, 4, "width height",},
919};
920static int nScreenOps = NumCmdSpecs(screenOps);
921
922static int
923ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
924          Tcl_Obj *const *objv)
925{
926    Tcl_ObjCmdProc *proc;
927
928    proc = Rappture::GetOpFromObj(interp, nScreenOps, screenOps,
929                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
930    if (proc == NULL) {
931        return TCL_ERROR;
932    }
933    return (*proc) (clientData, interp, objc, objv);
934}
935
936/*
937 * ----------------------------------------------------------------------
938 * CLIENT COMMAND:
939 *   transfunc define <name> <colormap> <alphamap>
940 *     where <colormap> = { <v> <r> <g> <b> ... }
941 *           <alphamap> = { <v> <w> ... }
942 *
943 * Clients send these commands to manipulate the transfer functions.
944 * ----------------------------------------------------------------------
945 */
946static int
947TransfuncCmd(ClientData clientData, Tcl_Interp *interp, int objc,
948             Tcl_Obj *const *objv)
949{
950    if (objc < 2) {
951        Tcl_AppendResult(interp, "wrong # args: should be \"",
952                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
953        return TCL_ERROR;
954    }
955
956    const char *string = Tcl_GetString(objv[1]);
957    char c = string[0];
958    if ((c == 'd') && (strcmp(string, "define") == 0)) {
959        if (objc != 5) {
960            Tcl_AppendResult(interp, "wrong # args: should be \"",
961                Tcl_GetString(objv[0]), " define name colorMap alphaMap\"",
962                (char*)NULL);
963            return TCL_ERROR;
964        }
965
966        // decode the data and store in a series of fields
967        Rappture::Field1D rFunc, gFunc, bFunc, wFunc;
968        int cmapc, wmapc, i;
969        Tcl_Obj **cmapv;
970        Tcl_Obj **wmapv;
971
972        wmapv = cmapv = NULL;
973        if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
974            return TCL_ERROR;
975        }
976        if ((cmapc % 4) != 0) {
977            Tcl_AppendResult(interp, "wrong # elements is colormap: should be ",
978                "{ v r g b ... }", (char*)NULL);
979            return TCL_ERROR;
980        }
981        if (Tcl_ListObjGetElements(interp, objv[4], &wmapc, &wmapv) != TCL_OK) {
982            return TCL_ERROR;
983        }
984        if ((wmapc % 2) != 0) {
985            Tcl_AppendResult(interp, "wrong # elements in alphamap: should be ",
986                " { v w ... }", (char*)NULL);
987            return TCL_ERROR;
988        }
989        for (i = 0; i < cmapc; i += 4) {
990            int j;
991            double q[4];
992
993            for (j=0; j < 4; j++) {
994                if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &q[j]) != TCL_OK) {
995                    return TCL_ERROR;
996                }
997                if ((q[j] < 0.0) || (q[j] > 1.0)) {
998                    Tcl_AppendResult(interp, "bad colormap value \"",
999                        Tcl_GetString(cmapv[i+j]),
1000                        "\": should be in the range 0-1", (char*)NULL);
1001                    return TCL_ERROR;
1002                }
1003            }
1004            rFunc.define(q[0], q[1]);
1005            gFunc.define(q[0], q[2]);
1006            bFunc.define(q[0], q[3]);
1007        }
1008        for (i=0; i < wmapc; i += 2) {
1009            double q[2];
1010            int j;
1011
1012            for (j=0; j < 2; j++) {
1013                if (Tcl_GetDoubleFromObj(interp, wmapv[i+j], &q[j]) != TCL_OK) {
1014                    return TCL_ERROR;
1015                }
1016                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1017                    Tcl_AppendResult(interp, "bad alphamap value \"",
1018                        Tcl_GetString(wmapv[i+j]),
1019                        "\": should be in the range 0-1", (char*)NULL);
1020                    return TCL_ERROR;
1021                }
1022            }
1023            wFunc.define(q[0], q[1]);
1024        }
1025        // sample the given function into discrete slots
1026        const int nslots = 256;
1027        float data[4*nslots];
1028        for (i=0; i < nslots; i++) {
1029            double x = double(i)/(nslots-1);
1030            data[4*i]   = rFunc.value(x);
1031            data[4*i+1] = gFunc.value(x);
1032            data[4*i+2] = bFunc.value(x);
1033            data[4*i+3] = wFunc.value(x);
1034        }
1035        // find or create this transfer function
1036        NanoVis::defineTransferFunction(Tcl_GetString(objv[2]), nslots, data);
1037    } else {
1038        Tcl_AppendResult(interp, "bad option \"", string,
1039                "\": should be define", (char*)NULL);
1040        return TCL_ERROR;
1041    }
1042    return TCL_OK;
1043}
1044
1045/*
1046 * ----------------------------------------------------------------------
1047 * CLIENT COMMAND:
1048 *   up axis
1049 *
1050 * Clients use this to set the "up" direction for all volumes.  Volumes
1051 * are oriented such that this direction points upward.
1052 * ----------------------------------------------------------------------
1053 */
1054static int
1055UpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1056{
1057    if (objc != 2) {
1058        Tcl_AppendResult(interp, "wrong # args: should be \"",
1059                         Tcl_GetString(objv[0]), " x|y|z|-x|-y|-z\"", (char*)NULL);
1060        return TCL_ERROR;
1061    }
1062
1063    int sign;
1064    int axis;
1065    if (GetAxisDirFromObj(interp, objv[1], &axis, &sign) != TCL_OK) {
1066        return TCL_ERROR;
1067    }
1068    NanoVis::updir = (axis+1)*sign;
1069    return TCL_OK;
1070}
1071
1072static int
1073VolumeAnimationCaptureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1074                         Tcl_Obj *const *objv)
1075{
1076    int total;
1077    if (Tcl_GetIntFromObj(interp, objv[3], &total) != TCL_OK) {
1078        return TCL_ERROR;
1079    }
1080    VolumeInterpolator* interpolator;
1081    interpolator = NanoVis::volRenderer->getVolumeInterpolator();
1082    interpolator->start();
1083    if (interpolator->isStarted()) {
1084        const char *fileName = (objc < 5) ? NULL : Tcl_GetString(objv[4]);
1085        for (int frame_num = 0; frame_num < total; ++frame_num) {
1086            float fraction;
1087
1088            fraction = ((float)frame_num) / (total - 1);
1089            TRACE("fraction : %f", fraction);
1090            //interpolator->update(((float)frame_num) / (total - 1));
1091            interpolator->update(fraction);
1092
1093            int fboOrig;
1094            glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboOrig);
1095
1096            NanoVis::bindOffscreenBuffer();  //enable offscreen render
1097
1098            NanoVis::render();
1099            NanoVis::readScreen();
1100
1101            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboOrig);
1102
1103            NanoVis::bmpWriteToFile(frame_num, fileName);
1104        }
1105    }
1106    return TCL_OK;
1107}
1108
1109static int
1110VolumeAnimationClearOp(ClientData clientData, Tcl_Interp *interp, int objc,
1111                       Tcl_Obj *const *objv)
1112{
1113    NanoVis::volRenderer->clearAnimatedVolumeInfo();
1114    return TCL_OK;
1115}
1116
1117static int
1118VolumeAnimationStartOp(ClientData clientData, Tcl_Interp *interp, int objc,
1119                       Tcl_Obj *const *objv)
1120{
1121    NanoVis::volRenderer->startVolumeAnimation();
1122    return TCL_OK;
1123}
1124
1125static int
1126VolumeAnimationStopOp(ClientData clientData, Tcl_Interp *interp, int objc,
1127                      Tcl_Obj *const *objv)
1128{
1129    NanoVis::volRenderer->stopVolumeAnimation();
1130    return TCL_OK;
1131}
1132
1133static int
1134VolumeAnimationVolumesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1135                         Tcl_Obj *const *objv)
1136{
1137    std::vector<Volume *> volumes;
1138    if (GetVolumes(interp, objc - 3, objv + 3, &volumes) != TCL_OK) {
1139        return TCL_ERROR;
1140    }
1141    TRACE("parsing volume data identifier");
1142    NanoVis::VolumeHashmap::iterator itr;
1143    for (itr = NanoVis::volumeTable.begin();
1144         itr != NanoVis::volumeTable.end(); ++itr) {
1145        NanoVis::volRenderer->addAnimatedVolume(itr->second);
1146    }
1147    return TCL_OK;
1148}
1149
1150static Rappture::CmdSpec volumeAnimationOps[] = {
1151    {"capture",   2, VolumeAnimationCaptureOp,  4, 5, "numframes ?filename?",},
1152    {"clear",     2, VolumeAnimationClearOp,    3, 3, "",},
1153    {"start",     3, VolumeAnimationStartOp,    3, 3, "",},
1154    {"stop",      3, VolumeAnimationStopOp,     3, 3, "",},
1155    {"volumes",   1, VolumeAnimationVolumesOp,  3, 0, "?indices?",},
1156};
1157
1158static int nVolumeAnimationOps = NumCmdSpecs(volumeAnimationOps);
1159
1160static int
1161VolumeAnimationOp(ClientData clientData, Tcl_Interp *interp, int objc,
1162                  Tcl_Obj *const *objv)
1163{
1164    Tcl_ObjCmdProc *proc;
1165
1166    proc = Rappture::GetOpFromObj(interp, nVolumeAnimationOps, 
1167                volumeAnimationOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1168    if (proc == NULL) {
1169        return TCL_ERROR;
1170    }
1171    return (*proc) (clientData, interp, objc, objv);
1172}
1173
1174static int
1175VolumeDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1176                    Tcl_Obj *const *objv)
1177{
1178    TRACE("Data Loading");
1179
1180    int nbytes;
1181    if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
1182        return TCL_ERROR;
1183    }
1184    const char *tag = Tcl_GetString(objv[4]);
1185    Rappture::Buffer buf;
1186    if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
1187        return TCL_ERROR;
1188    }
1189    const char *bytes;
1190    size_t nBytes;
1191
1192    bytes = buf.bytes();
1193    nBytes = buf.size();
1194
1195    TRACE("Checking header[%.20s]", bytes);
1196
1197    Volume *volume = NULL;
1198
1199    if ((nBytes > 5) && (strncmp(bytes, "<HDR>", 5) == 0)) {
1200        TRACE("ZincBlende Stream loading...");
1201         //std::stringstream fdata(std::ios_base::out|std::ios_base::in|std::ios_base::binary);
1202        //fdata.write(buf.bytes(),buf.size());
1203        //vol = NvZincBlendeReconstructor::getInstance()->loadFromStream(fdata);
1204
1205        volume = NvZincBlendeReconstructor::getInstance()->loadFromMemory((void*) buf.bytes());
1206        if (volume == NULL) {
1207            Tcl_AppendResult(interp, "can't get volume instance", (char *)NULL);
1208            return TCL_ERROR;
1209        }
1210        TRACE("finish loading");
1211
1212        Vector3f scale = volume->getPhysicalScaling();
1213        Vector3f loc(scale);
1214        loc *= -0.5;
1215        volume->location(loc);
1216
1217        NanoVis::VolumeHashmap::iterator itr = NanoVis::volumeTable.find(tag);
1218        if (itr != NanoVis::volumeTable.end()) {
1219            Tcl_AppendResult(interp, "volume \"", tag, "\" already exists.",
1220                             (char *)NULL);
1221            return TCL_ERROR;
1222        }
1223        NanoVis::volumeTable[tag] = volume;
1224        volume->name(tag);
1225    } else if ((nBytes > 14) && (strncmp(bytes, "# vtk DataFile", 14) == 0)) {
1226        TRACE("VTK loading...");
1227        std::stringstream fdata;
1228        fdata.write(bytes, nBytes);
1229        if (nBytes <= 0) {
1230            ERROR("data buffer is empty");
1231            abort();
1232        }
1233        Rappture::Outcome context;
1234        volume = load_vtk_volume_stream(context, tag, fdata);
1235        if (volume == NULL) {
1236            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1237            return TCL_ERROR;
1238        }
1239    } else {
1240        // **Deprecated** OpenDX format
1241        if ((nBytes > 5) && (strncmp(bytes, "<ODX>", 5) == 0)) {
1242            bytes += 5;
1243            nBytes -= 5;
1244        }
1245        TRACE("DX loading...");
1246        std::stringstream fdata;
1247        fdata.write(bytes, nBytes);
1248        if (nBytes <= 0) {
1249            ERROR("data buffer is empty");
1250            abort();
1251        }
1252        Rappture::Outcome context;
1253        volume = load_dx_volume_stream(context, tag, fdata);
1254        if (volume == NULL) {
1255            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1256            return TCL_ERROR;
1257        }
1258    }
1259
1260    if (volume != NULL) {
1261        volume->disableCutplane(0);
1262        volume->disableCutplane(1);
1263        volume->disableCutplane(2);
1264        volume->transferFunction(NanoVis::getTransferFunction("default"));
1265        volume->visible(true);
1266
1267        char info[1024];
1268        ssize_t nWritten;
1269
1270        if (Volume::updatePending) {
1271            NanoVis::setVolumeRanges();
1272        }
1273
1274        // FIXME: strlen(info) is the return value of sprintf
1275        sprintf(info, "nv>data tag %s min %g max %g vmin %g vmax %g\n", tag, 
1276                volume->wAxis.min(), volume->wAxis.max(),
1277                Volume::valueMin, Volume::valueMax);
1278        nWritten  = write(1, info, strlen(info));
1279        assert(nWritten == (ssize_t)strlen(info));
1280    }
1281    return TCL_OK;
1282}
1283
1284static int
1285VolumeDataStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1286                  Tcl_Obj *const *objv)
1287{
1288    bool state;
1289    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1290        return TCL_ERROR;
1291    }
1292    std::vector<Volume *> ivol;
1293    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1294        return TCL_ERROR;
1295    }
1296    std::vector<Volume *>::iterator iter;
1297    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1298        (*iter)->dataEnabled(state);
1299    }
1300    return TCL_OK;
1301}
1302
1303static Rappture::CmdSpec volumeDataOps[] = {
1304    {"follows",   1, VolumeDataFollowsOp, 5, 5, "size tag",},
1305    {"state",     1, VolumeDataStateOp,   4, 0, "bool ?indices?",},
1306};
1307static int nVolumeDataOps = NumCmdSpecs(volumeDataOps);
1308
1309static int
1310VolumeDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1311             Tcl_Obj *const *objv)
1312{
1313    Tcl_ObjCmdProc *proc;
1314
1315    proc = Rappture::GetOpFromObj(interp, nVolumeDataOps, volumeDataOps,
1316                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1317    if (proc == NULL) {
1318        return TCL_ERROR;
1319    }
1320    return (*proc) (clientData, interp, objc, objv);
1321}
1322
1323static int
1324VolumeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1325               Tcl_Obj *const *objv)
1326{
1327    for (int i = 2; i < objc; i++) {
1328        Volume *volume;
1329        if (GetVolumeFromObj(interp, objv[i], &volume) != TCL_OK) {
1330            return TCL_ERROR;
1331        }
1332        NanoVis::removeVolume(volume);
1333    }
1334    NanoVis::eventuallyRedraw();
1335    return TCL_OK;
1336}
1337
1338static int
1339VolumeExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1340               Tcl_Obj *const *objv)
1341{
1342    bool value;
1343    Volume *volume;
1344
1345    value = false;
1346    if (GetVolumeFromObj(NULL, objv[2], &volume) == TCL_OK) {
1347        value = true;
1348    }
1349    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
1350    return TCL_OK;
1351}
1352
1353static int
1354VolumeNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1355              Tcl_Obj *const *objv)
1356{
1357    Tcl_Obj *listObjPtr;
1358    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1359    NanoVis::VolumeHashmap::iterator itr;
1360    for (itr = NanoVis::volumeTable.begin();
1361         itr != NanoVis::volumeTable.end(); ++itr) {
1362        Tcl_Obj *objPtr = Tcl_NewStringObj(itr->second->name(), -1);
1363        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1364    }
1365    Tcl_SetObjResult(interp, listObjPtr);
1366    return TCL_OK;
1367}
1368
1369static int
1370VolumeOutlineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1371                     Tcl_Obj *const *objv)
1372{
1373    float rgb[3];
1374    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1375        return TCL_ERROR;
1376    }
1377    std::vector<Volume *> ivol;
1378    if (GetVolumes(interp, objc - 6, objv + 6, &ivol) != TCL_OK) {
1379        return TCL_ERROR;
1380    }
1381    std::vector<Volume *>::iterator iter;
1382    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1383        (*iter)->setOutlineColor(rgb);
1384    }
1385    return TCL_OK;
1386}
1387
1388static int
1389VolumeOutlineStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1390                     Tcl_Obj *const *objv)
1391{
1392    bool state;
1393    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1394        return TCL_ERROR;
1395    }
1396    std::vector<Volume *> ivol;
1397    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1398        return TCL_ERROR;
1399    }
1400    std::vector<Volume *>::iterator iter;
1401    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1402        (*iter)->outline(state);
1403    }
1404    return TCL_OK;
1405}
1406
1407
1408static Rappture::CmdSpec volumeOutlineOps[] = {
1409    {"color",     1, VolumeOutlineColorOp,  6, 0, "r g b ?indices?",},
1410    {"state",     1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1411    {"visible",   1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1412};
1413static int nVolumeOutlineOps = NumCmdSpecs(volumeOutlineOps);
1414
1415static int
1416VolumeOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc,
1417                Tcl_Obj *const *objv)
1418{
1419    Tcl_ObjCmdProc *proc;
1420
1421    proc = Rappture::GetOpFromObj(interp, nVolumeOutlineOps, volumeOutlineOps,
1422        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1423    if (proc == NULL) {
1424        return TCL_ERROR;
1425    }
1426    return (*proc) (clientData, interp, objc, objv);
1427}
1428
1429static int
1430VolumeShadingAmbientOp(ClientData clientData, Tcl_Interp *interp, int objc,
1431                       Tcl_Obj *const *objv)
1432{
1433    float ambient;
1434    if (GetFloatFromObj(interp, objv[3], &ambient) != TCL_OK) {
1435        return TCL_ERROR;
1436    }
1437    if (ambient < 0.0f || ambient > 1.0f) {
1438        WARN("Invalid ambient coefficient requested: %g, should be [0,1]", ambient);
1439    }
1440    std::vector<Volume *> ivol;
1441    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1442        return TCL_ERROR;
1443    }
1444    std::vector<Volume *>::iterator iter;
1445    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1446        (*iter)->ambient(ambient);
1447    }
1448    return TCL_OK;
1449}
1450
1451static int
1452VolumeShadingDiffuseOp(ClientData clientData, Tcl_Interp *interp, int objc,
1453                       Tcl_Obj *const *objv)
1454{
1455    float diffuse;
1456    if (GetFloatFromObj(interp, objv[3], &diffuse) != TCL_OK) {
1457        return TCL_ERROR;
1458    }
1459    if (diffuse < 0.0f || diffuse > 1.0f) {
1460        WARN("Invalid diffuse coefficient requested: %g, should be [0,1]", diffuse);
1461    }
1462    std::vector<Volume *> ivol;
1463    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1464        return TCL_ERROR;
1465    }
1466    std::vector<Volume *>::iterator iter;
1467    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1468        (*iter)->diffuse(diffuse);
1469    }
1470    return TCL_OK;
1471}
1472
1473static int
1474VolumeShadingIsosurfaceOp(ClientData clientData, Tcl_Interp *interp, int objc,
1475                          Tcl_Obj *const *objv)
1476{
1477    bool iso_surface;
1478    if (GetBooleanFromObj(interp, objv[3], &iso_surface) != TCL_OK) {
1479        return TCL_ERROR;
1480    }
1481    std::vector<Volume *> ivol;
1482    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1483        return TCL_ERROR;
1484    }
1485    std::vector<Volume *>::iterator iter;
1486    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1487        (*iter)->isosurface(iso_surface);
1488    }
1489    return TCL_OK;
1490}
1491
1492static int
1493VolumeShadingLight2SideOp(ClientData clientData, Tcl_Interp *interp, int objc,
1494                          Tcl_Obj *const *objv)
1495{
1496    bool twoSidedLighting;
1497    if (GetBooleanFromObj(interp, objv[3], &twoSidedLighting) != TCL_OK) {
1498        return TCL_ERROR;
1499    }
1500    std::vector<Volume *> ivol;
1501    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1502        return TCL_ERROR;
1503    }
1504    std::vector<Volume *>::iterator iter;
1505    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1506        (*iter)->twoSidedLighting(twoSidedLighting);
1507    }
1508    return TCL_OK;
1509}
1510
1511static int
1512VolumeShadingOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1513                       Tcl_Obj *const *objv)
1514{
1515
1516    float opacity;
1517    if (GetFloatFromObj(interp, objv[3], &opacity) != TCL_OK) {
1518        return TCL_ERROR;
1519    }
1520    TRACE("set opacity %f", opacity);
1521    std::vector<Volume *> ivol;
1522    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1523        return TCL_ERROR;
1524    }
1525    std::vector<Volume *>::iterator iter;
1526    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1527        (*iter)->opacityScale(opacity);
1528    }
1529    return TCL_OK;
1530}
1531
1532static int
1533VolumeShadingSpecularOp(ClientData clientData, Tcl_Interp *interp, int objc,
1534                        Tcl_Obj *const *objv)
1535{
1536    float specular;
1537    if (GetFloatFromObj(interp, objv[3], &specular) != TCL_OK) {
1538        return TCL_ERROR;
1539    }
1540    if (specular < 0.0f || specular > 1.0f) {
1541        WARN("Invalid specular coefficient requested: %g, should be [0,1]", specular);
1542    }
1543    std::vector<Volume *> ivol;
1544    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1545        return TCL_ERROR;
1546    }
1547    std::vector<Volume *>::iterator iter;
1548    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1549        (*iter)->specularLevel(specular);
1550    }
1551    return TCL_OK;
1552}
1553
1554static int
1555VolumeShadingSpecularExpOp(ClientData clientData, Tcl_Interp *interp, int objc,
1556                           Tcl_Obj *const *objv)
1557{
1558    float specularExp;
1559    if (GetFloatFromObj(interp, objv[3], &specularExp) != TCL_OK) {
1560        return TCL_ERROR;
1561    }
1562    if (specularExp < 0.0f || specularExp > 128.0f) {
1563        WARN("Invalid specular exponent requested: %g, should be [0,128]", specularExp);
1564    }
1565    std::vector<Volume *> ivol;
1566    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1567        return TCL_ERROR;
1568    }
1569    std::vector<Volume *>::iterator iter;
1570    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1571        (*iter)->specularExponent(specularExp);
1572    }
1573    return TCL_OK;
1574}
1575
1576static int
1577VolumeShadingTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1578                         Tcl_Obj *const *objv)
1579{
1580    const char *name = Tcl_GetString(objv[3]);
1581    TransferFunction *tf = NanoVis::getTransferFunction(name);
1582    if (tf == NULL) {
1583        Tcl_AppendResult(interp, "transfer function \"", name,
1584                         "\" is not defined", (char*)NULL);
1585        return TCL_ERROR;
1586    }
1587    std::vector<Volume *> ivol;
1588    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1589        return TCL_ERROR;
1590    }
1591    std::vector<Volume *>::iterator iter;
1592    for (iter = ivol.begin(); iter != ivol.end(); ++iter) {
1593        TRACE("setting %s with transfer function %s", (*iter)->name(),
1594               tf->name());
1595        (*iter)->transferFunction(tf);
1596#ifdef USE_POINTSET_RENDERER
1597        // TBD..
1598        if ((*iter)->pointsetIndex != -1) {
1599            NanoVis::pointSet[(*iter)->pointsetIndex]->updateColor(tf->getData(), 256);
1600        }
1601#endif
1602    }
1603    return TCL_OK;
1604}
1605
1606static Rappture::CmdSpec volumeShadingOps[] = {
1607    {"ambient",       1, VolumeShadingAmbientOp,     4, 0, "value ?indices?",},
1608    {"diffuse",       1, VolumeShadingDiffuseOp,     4, 0, "value ?indices?",},
1609    {"isosurface",    1, VolumeShadingIsosurfaceOp,  4, 0, "bool ?indices?",},
1610    {"light2side",    1, VolumeShadingLight2SideOp,  4, 0, "bool ?indices?",},
1611    {"opacity",       1, VolumeShadingOpacityOp,     4, 0, "value ?indices?",},
1612    {"specularExp",   9, VolumeShadingSpecularExpOp, 4, 0, "value ?indices?",},
1613    {"specularLevel", 9, VolumeShadingSpecularOp,    4, 0, "value ?indices?",},
1614    {"transfunc",     1, VolumeShadingTransFuncOp,   4, 0, "funcName ?indices?",},
1615};
1616static int nVolumeShadingOps = NumCmdSpecs(volumeShadingOps);
1617
1618static int
1619VolumeShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1620                Tcl_Obj *const *objv)
1621{
1622    Tcl_ObjCmdProc *proc;
1623
1624    proc = Rappture::GetOpFromObj(interp, nVolumeShadingOps, volumeShadingOps,
1625        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1626    if (proc == NULL) {
1627        return TCL_ERROR;
1628    }
1629    return (*proc) (clientData, interp, objc, objv);
1630}
1631
1632static int
1633VolumeStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1634              Tcl_Obj *const *objv)
1635{
1636    bool state;
1637    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1638        return TCL_ERROR;
1639    }
1640    std::vector<Volume *> ivol;
1641    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
1642        return TCL_ERROR;
1643    }
1644    std::vector<Volume *>::iterator iter;
1645    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1646        (*iter)->visible(state);
1647    }
1648    return TCL_OK;
1649}
1650
1651static Rappture::CmdSpec volumeOps[] = {
1652    {"animation", 2, VolumeAnimationOp,   3, 0, "oper ?args?",},
1653    {"data",      2, VolumeDataOp,        3, 0, "oper ?args?",},
1654    {"delete",    2, VolumeDeleteOp,      3, 0, "?name...?",},
1655    {"exists",    1, VolumeExistsOp,      3, 3, "name",},
1656    {"names",     1, VolumeNamesOp,       2, 3, "?pattern?",},
1657    {"outline",   1, VolumeOutlineOp,     3, 0, "oper ?args?",},
1658    {"shading",   2, VolumeShadingOp,     3, 0, "oper ?args?",},
1659    {"state",     2, VolumeStateOp,       3, 0, "bool ?indices?",},
1660};
1661static int nVolumeOps = NumCmdSpecs(volumeOps);
1662
1663/*
1664 * ----------------------------------------------------------------------
1665 * CLIENT COMMAND:
1666 *   volume data state on|off ?<volumeId> ...?
1667 *   volume outline state on|off ?<volumeId> ...?
1668 *   volume outline color on|off ?<volumeId> ...?
1669 *   volume shading transfunc <name> ?<volumeId> ...?
1670 *   volume shading diffuse <value> ?<volumeId> ...?
1671 *   volume shading specular <value> ?<volumeId> ...?
1672 *   volume shading opacity <value> ?<volumeId> ...?
1673 *   volume state on|off ?<volumeId> ...?
1674 *
1675 * Clients send these commands to manipulate the volumes.
1676 * ----------------------------------------------------------------------
1677 */
1678static int
1679VolumeCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
1680          Tcl_Obj *const *objv)
1681{
1682    Tcl_ObjCmdProc *proc;
1683
1684    proc = Rappture::GetOpFromObj(interp, nVolumeOps, volumeOps,
1685        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1686    if (proc == NULL) {
1687        return TCL_ERROR;
1688    }
1689    return (*proc) (clientData, interp, objc, objv);
1690}
1691
1692static int
1693HeightMapDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1694                       Tcl_Obj *const *objv)
1695{
1696    int nBytes;
1697    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
1698        return TCL_ERROR;
1699    }
1700    const char *tag = Tcl_GetString(objv[4]);
1701
1702    Rappture::Buffer buf;
1703    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
1704        return TCL_ERROR;
1705    }
1706    Rappture::Unirect2d data(1);
1707    if (data.parseBuffer(interp, buf) != TCL_OK) {
1708        return TCL_ERROR;
1709    }
1710    if (data.nValues() == 0) {
1711        Tcl_AppendResult(interp, "no data found in stream", (char *)NULL);
1712        return TCL_ERROR;
1713    }
1714    if (!data.isInitialized()) {
1715        return TCL_ERROR;
1716    }
1717    HeightMap *heightMap;
1718    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(tag);
1719    if (itr != NanoVis::heightMapTable.end()) {
1720        heightMap = itr->second;
1721    } else {
1722        heightMap = new HeightMap();
1723        NanoVis::heightMapTable[tag] = heightMap;
1724    }
1725    TRACE("Number of heightmaps=%d", NanoVis::heightMapTable.size());
1726    // Must set units before the heights.
1727    heightMap->xAxis.units(data.xUnits());
1728    heightMap->yAxis.units(data.yUnits());
1729    heightMap->zAxis.units(data.vUnits());
1730    heightMap->wAxis.units(data.yUnits());
1731    heightMap->setHeight(data.xMin(), data.yMin(), data.xMax(), data.yMax(), 
1732                         data.xNum(), data.yNum(), data.transferValues());
1733    heightMap->transferFunction(NanoVis::getTransferFunction("default"));
1734    heightMap->setVisible(true);
1735    heightMap->setLineContourVisible(true);
1736    NanoVis::eventuallyRedraw();
1737    return TCL_OK;
1738}
1739
1740static int
1741HeightMapDataVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1742                       Tcl_Obj *const *objv)
1743{
1744    bool visible;
1745    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1746        return TCL_ERROR;
1747    }
1748    std::vector<HeightMap *> imap;
1749    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1750        return TCL_ERROR;
1751    }
1752    std::vector<HeightMap *>::iterator iter;
1753    for (iter = imap.begin(); iter != imap.end(); iter++) {
1754        (*iter)->setVisible(visible);
1755    }
1756    NanoVis::eventuallyRedraw();
1757    return TCL_OK;
1758}
1759
1760static Rappture::CmdSpec heightMapDataOps[] = {
1761    {"follows",  1, HeightMapDataFollowsOp, 5, 5, "size tag",},
1762    {"visible",  1, HeightMapDataVisibleOp, 4, 0, "bool ?indices?",},
1763};
1764static int nHeightMapDataOps = NumCmdSpecs(heightMapDataOps);
1765
1766static int
1767HeightMapDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1768                Tcl_Obj *const *objv)
1769{
1770    Tcl_ObjCmdProc *proc;
1771
1772    proc = Rappture::GetOpFromObj(interp, nHeightMapDataOps, heightMapDataOps,
1773                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1774    if (proc == NULL) {
1775        return TCL_ERROR;
1776    }
1777    return (*proc) (clientData, interp, objc, objv);
1778}
1779
1780
1781static int
1782HeightMapLineContourColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1783                            Tcl_Obj *const *objv)
1784{
1785    float rgb[3];
1786    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1787        return TCL_ERROR;
1788    }
1789    std::vector<HeightMap *> imap;
1790    if (GetHeightMaps(interp, objc - 6, objv + 6, &imap) != TCL_OK) {
1791        return TCL_ERROR;
1792    }
1793    std::vector<HeightMap *>::iterator iter;
1794    for (iter = imap.begin(); iter != imap.end(); iter++) {
1795        (*iter)->setLineContourColor(rgb);
1796    }
1797    NanoVis::eventuallyRedraw();
1798    return TCL_OK;
1799}
1800
1801static int
1802HeightMapLineContourVisibleOp(ClientData clientData, Tcl_Interp *interp, 
1803                              int objc, Tcl_Obj *const *objv)
1804{
1805    bool visible;
1806    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1807        return TCL_ERROR;
1808    }
1809    std::vector<HeightMap *> imap;
1810    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1811        return TCL_ERROR;
1812    }
1813    std::vector<HeightMap *>::iterator iter;
1814    for (iter = imap.begin(); iter != imap.end(); iter++) {
1815        (*iter)->setLineContourVisible(visible);
1816    }
1817    NanoVis::eventuallyRedraw();
1818    return TCL_OK;
1819}
1820
1821static Rappture::CmdSpec heightMapLineContourOps[] = {
1822    {"color",   1, HeightMapLineContourColorOp,   4, 4, "length",},
1823    {"visible", 1, HeightMapLineContourVisibleOp, 4, 0, "bool ?indices?",},
1824};
1825static int nHeightMapLineContourOps = NumCmdSpecs(heightMapLineContourOps);
1826
1827static int 
1828HeightMapLineContourOp(ClientData clientData, Tcl_Interp *interp, int objc,
1829                       Tcl_Obj *const *objv)
1830{
1831    Tcl_ObjCmdProc *proc;
1832
1833    proc = Rappture::GetOpFromObj(interp, nHeightMapLineContourOps,
1834        heightMapLineContourOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1835    if (proc == NULL) {
1836        return TCL_ERROR;
1837    }
1838    return (*proc) (clientData, interp, objc, objv);
1839}
1840
1841static int
1842HeightMapCullOp(ClientData clientData, Tcl_Interp *interp, int objc,
1843                Tcl_Obj *const *objv)
1844{
1845    RenderContext::CullMode mode;
1846    if (GetCullMode(interp, objv[2], &mode) != TCL_OK) {
1847        return TCL_ERROR;
1848    }
1849    NanoVis::renderContext->setCullMode(mode);
1850    NanoVis::eventuallyRedraw();
1851    return TCL_OK;
1852}
1853
1854static int
1855HeightMapCreateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1856                  Tcl_Obj *const *objv)
1857{
1858    const char *tag = Tcl_GetString(objv[2]);
1859    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(tag);
1860    if (itr != NanoVis::heightMapTable.end()) {
1861        Tcl_AppendResult(interp, "heightmap \"", tag, "\" already exists.",
1862                         (char *)NULL);
1863        return TCL_ERROR;
1864    }
1865    /* heightmap create xmin ymin xmax ymax xnum ynum values */
1866    HeightMap *heightMap = CreateHeightMap(clientData, interp, objc - 3, objv + 3);
1867    if (heightMap == NULL) {
1868        return TCL_ERROR;
1869    }
1870    NanoVis::heightMapTable[tag] = heightMap;
1871    NanoVis::eventuallyRedraw();
1872    TRACE("Number of heightmaps=%d", NanoVis::heightMapTable.size());
1873    return TCL_OK;
1874}
1875
1876static int
1877HeightMapLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
1878                  Tcl_Obj *const *objv)
1879{
1880    HeightMap *hmPtr;
1881    if (GetHeightMapFromObj(interp, objv[2], &hmPtr) != TCL_OK) {
1882        return TCL_ERROR;
1883    }
1884    const char *tag;
1885    tag = Tcl_GetString(objv[2]);
1886    TransferFunction *tfPtr;
1887    tfPtr = hmPtr->transferFunction();
1888    if (tfPtr == NULL) {
1889        Tcl_AppendResult(interp, "no transfer function defined for heightmap"
1890                         " \"", tag, "\"", (char*)NULL);
1891        return TCL_ERROR;
1892    }
1893    int w, h;
1894    if ((Tcl_GetIntFromObj(interp, objv[3], &w) != TCL_OK) ||
1895        (Tcl_GetIntFromObj(interp, objv[4], &h) != TCL_OK)) {
1896        return TCL_ERROR;
1897    }
1898    if (HeightMap::updatePending) {
1899        NanoVis::setHeightmapRanges();
1900    }
1901    NanoVis::renderLegend(tfPtr, HeightMap::valueMin, HeightMap::valueMax, 
1902                          w, h, tag);
1903    return TCL_OK;
1904}
1905
1906static int
1907HeightMapPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
1908                   Tcl_Obj *const *objv)
1909{
1910    RenderContext::PolygonMode mode;
1911    if (GetPolygonMode(interp, objv[2], &mode) != TCL_OK) {
1912        return TCL_ERROR;
1913    }
1914    NanoVis::renderContext->setPolygonMode(mode);
1915    NanoVis::eventuallyRedraw();
1916    return TCL_OK;
1917}
1918
1919static int
1920HeightMapShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1921                 Tcl_Obj *const *objv)
1922{
1923    RenderContext::ShadingModel model;
1924    if (GetShadingModel(interp, objv[2], &model) != TCL_OK) {
1925        return TCL_ERROR;
1926    }
1927    NanoVis::renderContext->setShadingModel(model);
1928    NanoVis::eventuallyRedraw();
1929    return TCL_OK;
1930}
1931
1932static int
1933HeightMapTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1934                     Tcl_Obj *const *objv)
1935{
1936    const char *name;
1937    name = Tcl_GetString(objv[2]);
1938    TransferFunction *tf = NanoVis::getTransferFunction(name);
1939    if (tf == NULL) {
1940        Tcl_AppendResult(interp, "transfer function \"", name,
1941                         "\" is not defined", (char*)NULL);
1942        return TCL_ERROR;
1943    }
1944    std::vector<HeightMap *> imap;
1945    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
1946        return TCL_ERROR;
1947    }
1948    std::vector<HeightMap *>::iterator iter;
1949    for (iter = imap.begin(); iter != imap.end(); iter++) {
1950        (*iter)->transferFunction(tf);
1951    }
1952    NanoVis::eventuallyRedraw();
1953    return TCL_OK;
1954}
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, "tag 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, "index width height",},
1982    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
1983    {"opacity",      1, HeightMapOpacityOp,     3, 0, "value ?heightmap...? ",},
1984    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
1985    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
1986    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?heightmap...?",},
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.