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

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

merge r3597 from trunk

  • Property svn:eol-style set to native
File size: 69.4 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    int f;
772
773    /* Use the initial client key value pairs as the parts for a generating
774     * a unique file name. */
775    f = NanoVis::getStatsFile(objv[1]);
776    if (f < 0) {
777        Tcl_AppendResult(interp, "can't open stats file: ", 
778                         Tcl_PosixError(interp), (char *)NULL);
779        return TCL_ERROR;
780    }
781#endif
782    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
783    Tcl_IncrRefCount(listObjPtr);
784    if (first) {
785        first = false;
786        objPtr = Tcl_NewStringObj("render_start", 12);
787        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
788        /* server */
789        objPtr = Tcl_NewStringObj("server", 6);
790        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
791        objPtr = Tcl_NewStringObj("nanovis", 7);
792        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
793        /* pid */
794        objPtr = Tcl_NewStringObj("pid", 3);
795        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
796        Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewIntObj(getpid()));
797        /* machine */
798        objPtr = Tcl_NewStringObj("machine", 7);
799        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
800        gethostname(buf, BUFSIZ-1);
801        buf[BUFSIZ-1] = '\0';
802        objPtr = Tcl_NewStringObj(buf, -1);
803        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
804    } else {
805        objPtr = Tcl_NewStringObj("render_info", 11);
806        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
807    }
808    Tcl_DStringInit(&ds);
809    /* date */
810    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4));
811    strcpy(buf, ctime(&NanoVis::startTime.tv_sec));
812    buf[strlen(buf) - 1] = '\0';
813    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1));
814    /* date_secs */
815    Tcl_ListObjAppendElement(interp, listObjPtr, 
816                Tcl_NewStringObj("date_secs", 9));
817    Tcl_ListObjAppendElement(interp, listObjPtr, 
818                Tcl_NewLongObj(NanoVis::startTime.tv_sec));
819    /* Client arguments. */
820    if (Tcl_ListObjGetElements(interp, objv[1], &numItems, &items) != TCL_OK) {
821        return TCL_ERROR;
822    }
823    for (i = 0; i < numItems; i++) {
824        Tcl_ListObjAppendElement(interp, listObjPtr, items[i]);
825    }
826    Tcl_DStringInit(&ds);
827    string = Tcl_GetStringFromObj(listObjPtr, &length);
828    Tcl_DStringAppend(&ds, string, length);
829    Tcl_DStringAppend(&ds, "\n", 1);
830#ifdef KEEPSTATS
831    result = NanoVis::writeToStatsFile(f, Tcl_DStringValue(&ds), 
832                                       Tcl_DStringLength(&ds));
833#else
834    TRACE("clientinfo: %s", Tcl_DStringValue(&ds));
835#endif
836    Tcl_DStringFree(&ds);
837    Tcl_DecrRefCount(listObjPtr);
838    return result;
839}
840
841/*
842 * ----------------------------------------------------------------------
843 * CLIENT COMMAND:
844 *   legend <volumeIndex> <width> <height>
845 *
846 * Clients use this to generate a legend image for the specified
847 * transfer function.  The legend image is a color gradient from 0
848 * to one, drawn in the given transfer function.  The resulting image
849 * is returned in the size <width> x <height>.
850 * ----------------------------------------------------------------------
851 */
852static int
853LegendCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
854          Tcl_Obj *const *objv)
855{
856    if (objc != 4) {
857        Tcl_AppendResult(interp, "wrong # args: should be \"",
858            Tcl_GetString(objv[0]), " transfunc width height\"", (char*)NULL);
859        return TCL_ERROR;
860    }
861
862    const char *name;
863    name = Tcl_GetString(objv[1]);
864    TransferFunction *tf = NanoVis::getTransferFunction(name);
865    if (tf == NULL) {
866        Tcl_AppendResult(interp, "unknown transfer function \"", name, "\"",
867                             (char*)NULL);
868        return TCL_ERROR;
869    }
870    int w, h;
871    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
872        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
873        return TCL_ERROR;
874    }
875    if (Volume::updatePending) {
876        NanoVis::setVolumeRanges();
877    }
878    NanoVis::renderLegend(tf, Volume::valueMin, Volume::valueMax, w, h, name);
879    return TCL_OK;
880}
881
882static int
883ScreenBgColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
884                Tcl_Obj *const *objv)
885{
886    float rgb[3];
887    if ((GetFloatFromObj(interp, objv[2], &rgb[0]) != TCL_OK) ||
888        (GetFloatFromObj(interp, objv[3], &rgb[1]) != TCL_OK) ||
889        (GetFloatFromObj(interp, objv[4], &rgb[2]) != TCL_OK)) {
890        return TCL_ERROR;
891    }
892    NanoVis::setBgColor(rgb);
893    return TCL_OK;
894}
895
896/*
897 * ----------------------------------------------------------------------
898 * CLIENT COMMAND:
899 *   screen size <width> <height>
900 *
901 * Clients send this command to set the size of the rendering area.
902 * Future images are generated at the specified width/height.
903 * ----------------------------------------------------------------------
904 */
905static int
906ScreenSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
907             Tcl_Obj *const *objv)
908{
909    int w, h;
910    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
911        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
912        return TCL_ERROR;
913    }
914    NanoVis::resizeOffscreenBuffer(w, h);
915    return TCL_OK;
916}
917
918static Rappture::CmdSpec screenOps[] = {
919    {"bgcolor",  1, ScreenBgColorOp,  5, 5, "r g b",},
920    {"size",     1, ScreenSizeOp, 4, 4, "width height",},
921};
922static int nScreenOps = NumCmdSpecs(screenOps);
923
924static int
925ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
926          Tcl_Obj *const *objv)
927{
928    Tcl_ObjCmdProc *proc;
929
930    proc = Rappture::GetOpFromObj(interp, nScreenOps, screenOps,
931                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
932    if (proc == NULL) {
933        return TCL_ERROR;
934    }
935    return (*proc) (clientData, interp, objc, objv);
936}
937
938/*
939 * ----------------------------------------------------------------------
940 * CLIENT COMMAND:
941 *   transfunc define <name> <colormap> <alphamap>
942 *     where <colormap> = { <v> <r> <g> <b> ... }
943 *           <alphamap> = { <v> <w> ... }
944 *
945 * Clients send these commands to manipulate the transfer functions.
946 * ----------------------------------------------------------------------
947 */
948static int
949TransfuncCmd(ClientData clientData, Tcl_Interp *interp, int objc,
950             Tcl_Obj *const *objv)
951{
952    if (objc < 2) {
953        Tcl_AppendResult(interp, "wrong # args: should be \"",
954                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
955        return TCL_ERROR;
956    }
957
958    const char *string = Tcl_GetString(objv[1]);
959    char c = string[0];
960    if ((c == 'd') && (strcmp(string, "define") == 0)) {
961        if (objc != 5) {
962            Tcl_AppendResult(interp, "wrong # args: should be \"",
963                Tcl_GetString(objv[0]), " define name colorMap alphaMap\"",
964                (char*)NULL);
965            return TCL_ERROR;
966        }
967
968        // decode the data and store in a series of fields
969        Rappture::Field1D rFunc, gFunc, bFunc, wFunc;
970        int cmapc, wmapc, i;
971        Tcl_Obj **cmapv;
972        Tcl_Obj **wmapv;
973
974        wmapv = cmapv = NULL;
975        if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
976            return TCL_ERROR;
977        }
978        if ((cmapc % 4) != 0) {
979            Tcl_AppendResult(interp, "wrong # elements is colormap: should be ",
980                "{ v r g b ... }", (char*)NULL);
981            return TCL_ERROR;
982        }
983        if (Tcl_ListObjGetElements(interp, objv[4], &wmapc, &wmapv) != TCL_OK) {
984            return TCL_ERROR;
985        }
986        if ((wmapc % 2) != 0) {
987            Tcl_AppendResult(interp, "wrong # elements in alphamap: should be ",
988                " { v w ... }", (char*)NULL);
989            return TCL_ERROR;
990        }
991        for (i = 0; i < cmapc; i += 4) {
992            int j;
993            double q[4];
994
995            for (j=0; j < 4; j++) {
996                if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &q[j]) != TCL_OK) {
997                    return TCL_ERROR;
998                }
999                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1000                    Tcl_AppendResult(interp, "bad colormap value \"",
1001                        Tcl_GetString(cmapv[i+j]),
1002                        "\": should be in the range 0-1", (char*)NULL);
1003                    return TCL_ERROR;
1004                }
1005            }
1006            rFunc.define(q[0], q[1]);
1007            gFunc.define(q[0], q[2]);
1008            bFunc.define(q[0], q[3]);
1009        }
1010        for (i=0; i < wmapc; i += 2) {
1011            double q[2];
1012            int j;
1013
1014            for (j=0; j < 2; j++) {
1015                if (Tcl_GetDoubleFromObj(interp, wmapv[i+j], &q[j]) != TCL_OK) {
1016                    return TCL_ERROR;
1017                }
1018                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1019                    Tcl_AppendResult(interp, "bad alphamap value \"",
1020                        Tcl_GetString(wmapv[i+j]),
1021                        "\": should be in the range 0-1", (char*)NULL);
1022                    return TCL_ERROR;
1023                }
1024            }
1025            wFunc.define(q[0], q[1]);
1026        }
1027        // sample the given function into discrete slots
1028        const int nslots = 256;
1029        float data[4*nslots];
1030        for (i=0; i < nslots; i++) {
1031            double x = double(i)/(nslots-1);
1032            data[4*i]   = rFunc.value(x);
1033            data[4*i+1] = gFunc.value(x);
1034            data[4*i+2] = bFunc.value(x);
1035            data[4*i+3] = wFunc.value(x);
1036        }
1037        // find or create this transfer function
1038        NanoVis::defineTransferFunction(Tcl_GetString(objv[2]), nslots, data);
1039    } else {
1040        Tcl_AppendResult(interp, "bad option \"", string,
1041                "\": should be define", (char*)NULL);
1042        return TCL_ERROR;
1043    }
1044    return TCL_OK;
1045}
1046
1047/*
1048 * ----------------------------------------------------------------------
1049 * CLIENT COMMAND:
1050 *   up axis
1051 *
1052 * Clients use this to set the "up" direction for all volumes.  Volumes
1053 * are oriented such that this direction points upward.
1054 * ----------------------------------------------------------------------
1055 */
1056static int
1057UpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1058{
1059    if (objc != 2) {
1060        Tcl_AppendResult(interp, "wrong # args: should be \"",
1061                         Tcl_GetString(objv[0]), " x|y|z|-x|-y|-z\"", (char*)NULL);
1062        return TCL_ERROR;
1063    }
1064
1065    int sign;
1066    int axis;
1067    if (GetAxisDirFromObj(interp, objv[1], &axis, &sign) != TCL_OK) {
1068        return TCL_ERROR;
1069    }
1070    NanoVis::updir = (axis+1)*sign;
1071    return TCL_OK;
1072}
1073
1074static int
1075VolumeAnimationCaptureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1076                         Tcl_Obj *const *objv)
1077{
1078    int total;
1079    if (Tcl_GetIntFromObj(interp, objv[3], &total) != TCL_OK) {
1080        return TCL_ERROR;
1081    }
1082    VolumeInterpolator* interpolator;
1083    interpolator = NanoVis::volRenderer->getVolumeInterpolator();
1084    interpolator->start();
1085    if (interpolator->isStarted()) {
1086        const char *fileName = (objc < 5) ? NULL : Tcl_GetString(objv[4]);
1087        for (int frame_num = 0; frame_num < total; ++frame_num) {
1088            float fraction;
1089
1090            fraction = ((float)frame_num) / (total - 1);
1091            TRACE("fraction : %f", fraction);
1092            //interpolator->update(((float)frame_num) / (total - 1));
1093            interpolator->update(fraction);
1094
1095            int fboOrig;
1096            glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboOrig);
1097
1098            NanoVis::bindOffscreenBuffer();  //enable offscreen render
1099
1100            NanoVis::render();
1101            NanoVis::readScreen();
1102
1103            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboOrig);
1104
1105            NanoVis::bmpWriteToFile(frame_num, fileName);
1106        }
1107    }
1108    return TCL_OK;
1109}
1110
1111static int
1112VolumeAnimationClearOp(ClientData clientData, Tcl_Interp *interp, int objc,
1113                       Tcl_Obj *const *objv)
1114{
1115    NanoVis::volRenderer->clearAnimatedVolumeInfo();
1116    return TCL_OK;
1117}
1118
1119static int
1120VolumeAnimationStartOp(ClientData clientData, Tcl_Interp *interp, int objc,
1121                       Tcl_Obj *const *objv)
1122{
1123    NanoVis::volRenderer->startVolumeAnimation();
1124    return TCL_OK;
1125}
1126
1127static int
1128VolumeAnimationStopOp(ClientData clientData, Tcl_Interp *interp, int objc,
1129                      Tcl_Obj *const *objv)
1130{
1131    NanoVis::volRenderer->stopVolumeAnimation();
1132    return TCL_OK;
1133}
1134
1135static int
1136VolumeAnimationVolumesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1137                         Tcl_Obj *const *objv)
1138{
1139    std::vector<Volume *> volumes;
1140    if (GetVolumes(interp, objc - 3, objv + 3, &volumes) != TCL_OK) {
1141        return TCL_ERROR;
1142    }
1143    TRACE("parsing volume data identifier");
1144    NanoVis::VolumeHashmap::iterator itr;
1145    for (itr = NanoVis::volumeTable.begin();
1146         itr != NanoVis::volumeTable.end(); ++itr) {
1147        NanoVis::volRenderer->addAnimatedVolume(itr->second);
1148    }
1149    return TCL_OK;
1150}
1151
1152static Rappture::CmdSpec volumeAnimationOps[] = {
1153    {"capture",   2, VolumeAnimationCaptureOp,  4, 5, "numframes ?filename?",},
1154    {"clear",     2, VolumeAnimationClearOp,    3, 3, "",},
1155    {"start",     3, VolumeAnimationStartOp,    3, 3, "",},
1156    {"stop",      3, VolumeAnimationStopOp,     3, 3, "",},
1157    {"volumes",   1, VolumeAnimationVolumesOp,  3, 0, "?indices?",},
1158};
1159
1160static int nVolumeAnimationOps = NumCmdSpecs(volumeAnimationOps);
1161
1162static int
1163VolumeAnimationOp(ClientData clientData, Tcl_Interp *interp, int objc,
1164                  Tcl_Obj *const *objv)
1165{
1166    Tcl_ObjCmdProc *proc;
1167
1168    proc = Rappture::GetOpFromObj(interp, nVolumeAnimationOps, 
1169                volumeAnimationOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1170    if (proc == NULL) {
1171        return TCL_ERROR;
1172    }
1173    return (*proc) (clientData, interp, objc, objv);
1174}
1175
1176static int
1177VolumeDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1178                    Tcl_Obj *const *objv)
1179{
1180    TRACE("Data Loading");
1181
1182    int nbytes;
1183    if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
1184        return TCL_ERROR;
1185    }
1186    const char *tag = Tcl_GetString(objv[4]);
1187    Rappture::Buffer buf;
1188    if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
1189        return TCL_ERROR;
1190    }
1191    const char *bytes;
1192    size_t nBytes;
1193
1194    bytes = buf.bytes();
1195    nBytes = buf.size();
1196
1197    TRACE("Checking header[%.20s]", bytes);
1198
1199    Volume *volume = NULL;
1200
1201    if ((nBytes > 5) && (strncmp(bytes, "<HDR>", 5) == 0)) {
1202        TRACE("ZincBlende Stream loading...");
1203         //std::stringstream fdata(std::ios_base::out|std::ios_base::in|std::ios_base::binary);
1204        //fdata.write(buf.bytes(),buf.size());
1205        //vol = NvZincBlendeReconstructor::getInstance()->loadFromStream(fdata);
1206
1207        volume = NvZincBlendeReconstructor::getInstance()->loadFromMemory((void*) buf.bytes());
1208        if (volume == NULL) {
1209            Tcl_AppendResult(interp, "can't get volume instance", (char *)NULL);
1210            return TCL_ERROR;
1211        }
1212        TRACE("finish loading");
1213
1214        Vector3f scale = volume->getPhysicalScaling();
1215        Vector3f loc(scale);
1216        loc *= -0.5;
1217        volume->location(loc);
1218
1219        NanoVis::VolumeHashmap::iterator itr = NanoVis::volumeTable.find(tag);
1220        if (itr != NanoVis::volumeTable.end()) {
1221            Tcl_AppendResult(interp, "volume \"", tag, "\" already exists.",
1222                             (char *)NULL);
1223            return TCL_ERROR;
1224        }
1225        NanoVis::volumeTable[tag] = volume;
1226        volume->name(tag);
1227    } else if ((nBytes > 14) && (strncmp(bytes, "# vtk DataFile", 14) == 0)) {
1228        TRACE("VTK loading...");
1229        std::stringstream fdata;
1230        fdata.write(bytes, nBytes);
1231        if (nBytes <= 0) {
1232            ERROR("data buffer is empty");
1233            abort();
1234        }
1235        Rappture::Outcome context;
1236        volume = load_vtk_volume_stream(context, tag, fdata);
1237        if (volume == NULL) {
1238            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1239            return TCL_ERROR;
1240        }
1241    } else {
1242        // **Deprecated** OpenDX format
1243        if ((nBytes > 5) && (strncmp(bytes, "<ODX>", 5) == 0)) {
1244            bytes += 5;
1245            nBytes -= 5;
1246        }
1247        TRACE("DX loading...");
1248        std::stringstream fdata;
1249        fdata.write(bytes, nBytes);
1250        if (nBytes <= 0) {
1251            ERROR("data buffer is empty");
1252            abort();
1253        }
1254        Rappture::Outcome context;
1255        volume = load_dx_volume_stream(context, tag, fdata);
1256        if (volume == NULL) {
1257            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1258            return TCL_ERROR;
1259        }
1260    }
1261
1262    if (volume != NULL) {
1263        volume->disableCutplane(0);
1264        volume->disableCutplane(1);
1265        volume->disableCutplane(2);
1266        volume->transferFunction(NanoVis::getTransferFunction("default"));
1267        volume->visible(true);
1268
1269        char info[1024];
1270        ssize_t nWritten;
1271
1272        if (Volume::updatePending) {
1273            NanoVis::setVolumeRanges();
1274        }
1275
1276        // FIXME: strlen(info) is the return value of sprintf
1277        sprintf(info, "nv>data tag %s min %g max %g vmin %g vmax %g\n", tag, 
1278                volume->wAxis.min(), volume->wAxis.max(),
1279                Volume::valueMin, Volume::valueMax);
1280        nWritten  = write(1, info, strlen(info));
1281        assert(nWritten == (ssize_t)strlen(info));
1282    }
1283    return TCL_OK;
1284}
1285
1286static int
1287VolumeDataStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1288                  Tcl_Obj *const *objv)
1289{
1290    bool state;
1291    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1292        return TCL_ERROR;
1293    }
1294    std::vector<Volume *> ivol;
1295    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1296        return TCL_ERROR;
1297    }
1298    std::vector<Volume *>::iterator iter;
1299    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1300        (*iter)->dataEnabled(state);
1301    }
1302    return TCL_OK;
1303}
1304
1305static Rappture::CmdSpec volumeDataOps[] = {
1306    {"follows",   1, VolumeDataFollowsOp, 5, 5, "size tag",},
1307    {"state",     1, VolumeDataStateOp,   4, 0, "bool ?indices?",},
1308};
1309static int nVolumeDataOps = NumCmdSpecs(volumeDataOps);
1310
1311static int
1312VolumeDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1313             Tcl_Obj *const *objv)
1314{
1315    Tcl_ObjCmdProc *proc;
1316
1317    proc = Rappture::GetOpFromObj(interp, nVolumeDataOps, volumeDataOps,
1318                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1319    if (proc == NULL) {
1320        return TCL_ERROR;
1321    }
1322    return (*proc) (clientData, interp, objc, objv);
1323}
1324
1325static int
1326VolumeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1327               Tcl_Obj *const *objv)
1328{
1329    for (int i = 2; i < objc; i++) {
1330        Volume *volume;
1331        if (GetVolumeFromObj(interp, objv[i], &volume) != TCL_OK) {
1332            return TCL_ERROR;
1333        }
1334        NanoVis::removeVolume(volume);
1335    }
1336    NanoVis::eventuallyRedraw();
1337    return TCL_OK;
1338}
1339
1340static int
1341VolumeExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1342               Tcl_Obj *const *objv)
1343{
1344    bool value;
1345    Volume *volume;
1346
1347    value = false;
1348    if (GetVolumeFromObj(NULL, objv[2], &volume) == TCL_OK) {
1349        value = true;
1350    }
1351    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
1352    return TCL_OK;
1353}
1354
1355static int
1356VolumeNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1357              Tcl_Obj *const *objv)
1358{
1359    Tcl_Obj *listObjPtr;
1360    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1361    NanoVis::VolumeHashmap::iterator itr;
1362    for (itr = NanoVis::volumeTable.begin();
1363         itr != NanoVis::volumeTable.end(); ++itr) {
1364        Tcl_Obj *objPtr = Tcl_NewStringObj(itr->second->name(), -1);
1365        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1366    }
1367    Tcl_SetObjResult(interp, listObjPtr);
1368    return TCL_OK;
1369}
1370
1371static int
1372VolumeOutlineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1373                     Tcl_Obj *const *objv)
1374{
1375    float rgb[3];
1376    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1377        return TCL_ERROR;
1378    }
1379    std::vector<Volume *> ivol;
1380    if (GetVolumes(interp, objc - 6, objv + 6, &ivol) != TCL_OK) {
1381        return TCL_ERROR;
1382    }
1383    std::vector<Volume *>::iterator iter;
1384    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1385        (*iter)->setOutlineColor(rgb);
1386    }
1387    return TCL_OK;
1388}
1389
1390static int
1391VolumeOutlineStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1392                     Tcl_Obj *const *objv)
1393{
1394    bool state;
1395    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1396        return TCL_ERROR;
1397    }
1398    std::vector<Volume *> ivol;
1399    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1400        return TCL_ERROR;
1401    }
1402    std::vector<Volume *>::iterator iter;
1403    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1404        (*iter)->outline(state);
1405    }
1406    return TCL_OK;
1407}
1408
1409
1410static Rappture::CmdSpec volumeOutlineOps[] = {
1411    {"color",     1, VolumeOutlineColorOp,  6, 0, "r g b ?indices?",},
1412    {"state",     1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1413    {"visible",   1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1414};
1415static int nVolumeOutlineOps = NumCmdSpecs(volumeOutlineOps);
1416
1417static int
1418VolumeOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc,
1419                Tcl_Obj *const *objv)
1420{
1421    Tcl_ObjCmdProc *proc;
1422
1423    proc = Rappture::GetOpFromObj(interp, nVolumeOutlineOps, volumeOutlineOps,
1424        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1425    if (proc == NULL) {
1426        return TCL_ERROR;
1427    }
1428    return (*proc) (clientData, interp, objc, objv);
1429}
1430
1431static int
1432VolumeShadingAmbientOp(ClientData clientData, Tcl_Interp *interp, int objc,
1433                       Tcl_Obj *const *objv)
1434{
1435    float ambient;
1436    if (GetFloatFromObj(interp, objv[3], &ambient) != TCL_OK) {
1437        return TCL_ERROR;
1438    }
1439    if (ambient < 0.0f || ambient > 1.0f) {
1440        WARN("Invalid ambient coefficient requested: %g, should be [0,1]", ambient);
1441    }
1442    std::vector<Volume *> ivol;
1443    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1444        return TCL_ERROR;
1445    }
1446    std::vector<Volume *>::iterator iter;
1447    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1448        (*iter)->ambient(ambient);
1449    }
1450    return TCL_OK;
1451}
1452
1453static int
1454VolumeShadingDiffuseOp(ClientData clientData, Tcl_Interp *interp, int objc,
1455                       Tcl_Obj *const *objv)
1456{
1457    float diffuse;
1458    if (GetFloatFromObj(interp, objv[3], &diffuse) != TCL_OK) {
1459        return TCL_ERROR;
1460    }
1461    if (diffuse < 0.0f || diffuse > 1.0f) {
1462        WARN("Invalid diffuse coefficient requested: %g, should be [0,1]", diffuse);
1463    }
1464    std::vector<Volume *> ivol;
1465    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1466        return TCL_ERROR;
1467    }
1468    std::vector<Volume *>::iterator iter;
1469    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1470        (*iter)->diffuse(diffuse);
1471    }
1472    return TCL_OK;
1473}
1474
1475static int
1476VolumeShadingIsosurfaceOp(ClientData clientData, Tcl_Interp *interp, int objc,
1477                          Tcl_Obj *const *objv)
1478{
1479    bool iso_surface;
1480    if (GetBooleanFromObj(interp, objv[3], &iso_surface) != TCL_OK) {
1481        return TCL_ERROR;
1482    }
1483    std::vector<Volume *> ivol;
1484    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1485        return TCL_ERROR;
1486    }
1487    std::vector<Volume *>::iterator iter;
1488    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1489        (*iter)->isosurface(iso_surface);
1490    }
1491    return TCL_OK;
1492}
1493
1494static int
1495VolumeShadingLight2SideOp(ClientData clientData, Tcl_Interp *interp, int objc,
1496                          Tcl_Obj *const *objv)
1497{
1498    bool twoSidedLighting;
1499    if (GetBooleanFromObj(interp, objv[3], &twoSidedLighting) != TCL_OK) {
1500        return TCL_ERROR;
1501    }
1502    std::vector<Volume *> ivol;
1503    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1504        return TCL_ERROR;
1505    }
1506    std::vector<Volume *>::iterator iter;
1507    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1508        (*iter)->twoSidedLighting(twoSidedLighting);
1509    }
1510    return TCL_OK;
1511}
1512
1513static int
1514VolumeShadingOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1515                       Tcl_Obj *const *objv)
1516{
1517
1518    float opacity;
1519    if (GetFloatFromObj(interp, objv[3], &opacity) != TCL_OK) {
1520        return TCL_ERROR;
1521    }
1522    TRACE("set opacity %f", opacity);
1523    std::vector<Volume *> ivol;
1524    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1525        return TCL_ERROR;
1526    }
1527    std::vector<Volume *>::iterator iter;
1528    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1529        (*iter)->opacityScale(opacity);
1530    }
1531    return TCL_OK;
1532}
1533
1534static int
1535VolumeShadingSpecularOp(ClientData clientData, Tcl_Interp *interp, int objc,
1536                        Tcl_Obj *const *objv)
1537{
1538    float specular;
1539    if (GetFloatFromObj(interp, objv[3], &specular) != TCL_OK) {
1540        return TCL_ERROR;
1541    }
1542    if (specular < 0.0f || specular > 1.0f) {
1543        WARN("Invalid specular coefficient requested: %g, should be [0,1]", specular);
1544    }
1545    std::vector<Volume *> ivol;
1546    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1547        return TCL_ERROR;
1548    }
1549    std::vector<Volume *>::iterator iter;
1550    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1551        (*iter)->specularLevel(specular);
1552    }
1553    return TCL_OK;
1554}
1555
1556static int
1557VolumeShadingSpecularExpOp(ClientData clientData, Tcl_Interp *interp, int objc,
1558                           Tcl_Obj *const *objv)
1559{
1560    float specularExp;
1561    if (GetFloatFromObj(interp, objv[3], &specularExp) != TCL_OK) {
1562        return TCL_ERROR;
1563    }
1564    if (specularExp < 0.0f || specularExp > 128.0f) {
1565        WARN("Invalid specular exponent requested: %g, should be [0,128]", specularExp);
1566    }
1567    std::vector<Volume *> ivol;
1568    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1569        return TCL_ERROR;
1570    }
1571    std::vector<Volume *>::iterator iter;
1572    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1573        (*iter)->specularExponent(specularExp);
1574    }
1575    return TCL_OK;
1576}
1577
1578static int
1579VolumeShadingTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1580                         Tcl_Obj *const *objv)
1581{
1582    const char *name = Tcl_GetString(objv[3]);
1583    TransferFunction *tf = NanoVis::getTransferFunction(name);
1584    if (tf == NULL) {
1585        Tcl_AppendResult(interp, "transfer function \"", name,
1586                         "\" is not defined", (char*)NULL);
1587        return TCL_ERROR;
1588    }
1589    std::vector<Volume *> ivol;
1590    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1591        return TCL_ERROR;
1592    }
1593    std::vector<Volume *>::iterator iter;
1594    for (iter = ivol.begin(); iter != ivol.end(); ++iter) {
1595        TRACE("setting %s with transfer function %s", (*iter)->name(),
1596               tf->name());
1597        (*iter)->transferFunction(tf);
1598#ifdef USE_POINTSET_RENDERER
1599        // TBD..
1600        if ((*iter)->pointsetIndex != -1) {
1601            NanoVis::pointSet[(*iter)->pointsetIndex]->updateColor(tf->getData(), 256);
1602        }
1603#endif
1604    }
1605    return TCL_OK;
1606}
1607
1608static Rappture::CmdSpec volumeShadingOps[] = {
1609    {"ambient",       1, VolumeShadingAmbientOp,     4, 0, "value ?indices?",},
1610    {"diffuse",       1, VolumeShadingDiffuseOp,     4, 0, "value ?indices?",},
1611    {"isosurface",    1, VolumeShadingIsosurfaceOp,  4, 0, "bool ?indices?",},
1612    {"light2side",    1, VolumeShadingLight2SideOp,  4, 0, "bool ?indices?",},
1613    {"opacity",       1, VolumeShadingOpacityOp,     4, 0, "value ?indices?",},
1614    {"specularExp",   9, VolumeShadingSpecularExpOp, 4, 0, "value ?indices?",},
1615    {"specularLevel", 9, VolumeShadingSpecularOp,    4, 0, "value ?indices?",},
1616    {"transfunc",     1, VolumeShadingTransFuncOp,   4, 0, "funcName ?indices?",},
1617};
1618static int nVolumeShadingOps = NumCmdSpecs(volumeShadingOps);
1619
1620static int
1621VolumeShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1622                Tcl_Obj *const *objv)
1623{
1624    Tcl_ObjCmdProc *proc;
1625
1626    proc = Rappture::GetOpFromObj(interp, nVolumeShadingOps, volumeShadingOps,
1627        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1628    if (proc == NULL) {
1629        return TCL_ERROR;
1630    }
1631    return (*proc) (clientData, interp, objc, objv);
1632}
1633
1634static int
1635VolumeStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1636              Tcl_Obj *const *objv)
1637{
1638    bool state;
1639    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1640        return TCL_ERROR;
1641    }
1642    std::vector<Volume *> ivol;
1643    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
1644        return TCL_ERROR;
1645    }
1646    std::vector<Volume *>::iterator iter;
1647    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1648        (*iter)->visible(state);
1649    }
1650    return TCL_OK;
1651}
1652
1653static Rappture::CmdSpec volumeOps[] = {
1654    {"animation", 2, VolumeAnimationOp,   3, 0, "oper ?args?",},
1655    {"data",      2, VolumeDataOp,        3, 0, "oper ?args?",},
1656    {"delete",    2, VolumeDeleteOp,      3, 0, "?name...?",},
1657    {"exists",    1, VolumeExistsOp,      3, 3, "name",},
1658    {"names",     1, VolumeNamesOp,       2, 3, "?pattern?",},
1659    {"outline",   1, VolumeOutlineOp,     3, 0, "oper ?args?",},
1660    {"shading",   2, VolumeShadingOp,     3, 0, "oper ?args?",},
1661    {"state",     2, VolumeStateOp,       3, 0, "bool ?indices?",},
1662};
1663static int nVolumeOps = NumCmdSpecs(volumeOps);
1664
1665/*
1666 * ----------------------------------------------------------------------
1667 * CLIENT COMMAND:
1668 *   volume data state on|off ?<volumeId> ...?
1669 *   volume outline state on|off ?<volumeId> ...?
1670 *   volume outline color on|off ?<volumeId> ...?
1671 *   volume shading transfunc <name> ?<volumeId> ...?
1672 *   volume shading diffuse <value> ?<volumeId> ...?
1673 *   volume shading specular <value> ?<volumeId> ...?
1674 *   volume shading opacity <value> ?<volumeId> ...?
1675 *   volume state on|off ?<volumeId> ...?
1676 *
1677 * Clients send these commands to manipulate the volumes.
1678 * ----------------------------------------------------------------------
1679 */
1680static int
1681VolumeCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
1682          Tcl_Obj *const *objv)
1683{
1684    Tcl_ObjCmdProc *proc;
1685
1686    proc = Rappture::GetOpFromObj(interp, nVolumeOps, volumeOps,
1687        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1688    if (proc == NULL) {
1689        return TCL_ERROR;
1690    }
1691    return (*proc) (clientData, interp, objc, objv);
1692}
1693
1694static int
1695HeightMapDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1696                       Tcl_Obj *const *objv)
1697{
1698    int nBytes;
1699    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
1700        return TCL_ERROR;
1701    }
1702    const char *tag = Tcl_GetString(objv[4]);
1703
1704    Rappture::Buffer buf;
1705    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
1706        return TCL_ERROR;
1707    }
1708    Rappture::Unirect2d data(1);
1709    if (data.parseBuffer(interp, buf) != TCL_OK) {
1710        return TCL_ERROR;
1711    }
1712    if (data.nValues() == 0) {
1713        Tcl_AppendResult(interp, "no data found in stream", (char *)NULL);
1714        return TCL_ERROR;
1715    }
1716    if (!data.isInitialized()) {
1717        return TCL_ERROR;
1718    }
1719    HeightMap *heightMap;
1720    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(tag);
1721    if (itr != NanoVis::heightMapTable.end()) {
1722        heightMap = itr->second;
1723    } else {
1724        heightMap = new HeightMap();
1725        NanoVis::heightMapTable[tag] = heightMap;
1726    }
1727    TRACE("Number of heightmaps=%d", NanoVis::heightMapTable.size());
1728    // Must set units before the heights.
1729    heightMap->xAxis.units(data.xUnits());
1730    heightMap->yAxis.units(data.yUnits());
1731    heightMap->zAxis.units(data.vUnits());
1732    heightMap->wAxis.units(data.yUnits());
1733    heightMap->setHeight(data.xMin(), data.yMin(), data.xMax(), data.yMax(), 
1734                         data.xNum(), data.yNum(), data.transferValues());
1735    heightMap->transferFunction(NanoVis::getTransferFunction("default"));
1736    heightMap->setVisible(true);
1737    heightMap->setLineContourVisible(true);
1738    NanoVis::eventuallyRedraw();
1739    return TCL_OK;
1740}
1741
1742static int
1743HeightMapDataVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1744                       Tcl_Obj *const *objv)
1745{
1746    bool visible;
1747    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1748        return TCL_ERROR;
1749    }
1750    std::vector<HeightMap *> imap;
1751    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1752        return TCL_ERROR;
1753    }
1754    std::vector<HeightMap *>::iterator iter;
1755    for (iter = imap.begin(); iter != imap.end(); iter++) {
1756        (*iter)->setVisible(visible);
1757    }
1758    NanoVis::eventuallyRedraw();
1759    return TCL_OK;
1760}
1761
1762static Rappture::CmdSpec heightMapDataOps[] = {
1763    {"follows",  1, HeightMapDataFollowsOp, 5, 5, "size tag",},
1764    {"visible",  1, HeightMapDataVisibleOp, 4, 0, "bool ?indices?",},
1765};
1766static int nHeightMapDataOps = NumCmdSpecs(heightMapDataOps);
1767
1768static int
1769HeightMapDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1770                Tcl_Obj *const *objv)
1771{
1772    Tcl_ObjCmdProc *proc;
1773
1774    proc = Rappture::GetOpFromObj(interp, nHeightMapDataOps, heightMapDataOps,
1775                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1776    if (proc == NULL) {
1777        return TCL_ERROR;
1778    }
1779    return (*proc) (clientData, interp, objc, objv);
1780}
1781
1782
1783static int
1784HeightMapLineContourColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1785                            Tcl_Obj *const *objv)
1786{
1787    float rgb[3];
1788    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1789        return TCL_ERROR;
1790    }
1791    std::vector<HeightMap *> imap;
1792    if (GetHeightMaps(interp, objc - 6, objv + 6, &imap) != TCL_OK) {
1793        return TCL_ERROR;
1794    }
1795    std::vector<HeightMap *>::iterator iter;
1796    for (iter = imap.begin(); iter != imap.end(); iter++) {
1797        (*iter)->setLineContourColor(rgb);
1798    }
1799    NanoVis::eventuallyRedraw();
1800    return TCL_OK;
1801}
1802
1803static int
1804HeightMapLineContourVisibleOp(ClientData clientData, Tcl_Interp *interp, 
1805                              int objc, Tcl_Obj *const *objv)
1806{
1807    bool visible;
1808    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1809        return TCL_ERROR;
1810    }
1811    std::vector<HeightMap *> imap;
1812    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1813        return TCL_ERROR;
1814    }
1815    std::vector<HeightMap *>::iterator iter;
1816    for (iter = imap.begin(); iter != imap.end(); iter++) {
1817        (*iter)->setLineContourVisible(visible);
1818    }
1819    NanoVis::eventuallyRedraw();
1820    return TCL_OK;
1821}
1822
1823static Rappture::CmdSpec heightMapLineContourOps[] = {
1824    {"color",   1, HeightMapLineContourColorOp,   4, 4, "length",},
1825    {"visible", 1, HeightMapLineContourVisibleOp, 4, 0, "bool ?indices?",},
1826};
1827static int nHeightMapLineContourOps = NumCmdSpecs(heightMapLineContourOps);
1828
1829static int 
1830HeightMapLineContourOp(ClientData clientData, Tcl_Interp *interp, int objc,
1831                       Tcl_Obj *const *objv)
1832{
1833    Tcl_ObjCmdProc *proc;
1834
1835    proc = Rappture::GetOpFromObj(interp, nHeightMapLineContourOps,
1836        heightMapLineContourOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1837    if (proc == NULL) {
1838        return TCL_ERROR;
1839    }
1840    return (*proc) (clientData, interp, objc, objv);
1841}
1842
1843static int
1844HeightMapCullOp(ClientData clientData, Tcl_Interp *interp, int objc,
1845                Tcl_Obj *const *objv)
1846{
1847    RenderContext::CullMode mode;
1848    if (GetCullMode(interp, objv[2], &mode) != TCL_OK) {
1849        return TCL_ERROR;
1850    }
1851    NanoVis::renderContext->setCullMode(mode);
1852    NanoVis::eventuallyRedraw();
1853    return TCL_OK;
1854}
1855
1856static int
1857HeightMapCreateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1858                  Tcl_Obj *const *objv)
1859{
1860    const char *tag = Tcl_GetString(objv[2]);
1861    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(tag);
1862    if (itr != NanoVis::heightMapTable.end()) {
1863        Tcl_AppendResult(interp, "heightmap \"", tag, "\" already exists.",
1864                         (char *)NULL);
1865        return TCL_ERROR;
1866    }
1867    /* heightmap create xmin ymin xmax ymax xnum ynum values */
1868    HeightMap *heightMap = CreateHeightMap(clientData, interp, objc - 3, objv + 3);
1869    if (heightMap == NULL) {
1870        return TCL_ERROR;
1871    }
1872    NanoVis::heightMapTable[tag] = heightMap;
1873    NanoVis::eventuallyRedraw();
1874    TRACE("Number of heightmaps=%d", NanoVis::heightMapTable.size());
1875    return TCL_OK;
1876}
1877
1878static int
1879HeightMapLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
1880                  Tcl_Obj *const *objv)
1881{
1882    HeightMap *hmPtr;
1883    if (GetHeightMapFromObj(interp, objv[2], &hmPtr) != TCL_OK) {
1884        return TCL_ERROR;
1885    }
1886    const char *tag;
1887    tag = Tcl_GetString(objv[2]);
1888    TransferFunction *tfPtr;
1889    tfPtr = hmPtr->transferFunction();
1890    if (tfPtr == NULL) {
1891        Tcl_AppendResult(interp, "no transfer function defined for heightmap"
1892                         " \"", tag, "\"", (char*)NULL);
1893        return TCL_ERROR;
1894    }
1895    int w, h;
1896    if ((Tcl_GetIntFromObj(interp, objv[3], &w) != TCL_OK) ||
1897        (Tcl_GetIntFromObj(interp, objv[4], &h) != TCL_OK)) {
1898        return TCL_ERROR;
1899    }
1900    if (HeightMap::updatePending) {
1901        NanoVis::setHeightmapRanges();
1902    }
1903    NanoVis::renderLegend(tfPtr, HeightMap::valueMin, HeightMap::valueMax, 
1904                          w, h, tag);
1905    return TCL_OK;
1906}
1907
1908static int
1909HeightMapPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
1910                   Tcl_Obj *const *objv)
1911{
1912    RenderContext::PolygonMode mode;
1913    if (GetPolygonMode(interp, objv[2], &mode) != TCL_OK) {
1914        return TCL_ERROR;
1915    }
1916    NanoVis::renderContext->setPolygonMode(mode);
1917    NanoVis::eventuallyRedraw();
1918    return TCL_OK;
1919}
1920
1921static int
1922HeightMapShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1923                 Tcl_Obj *const *objv)
1924{
1925    RenderContext::ShadingModel model;
1926    if (GetShadingModel(interp, objv[2], &model) != TCL_OK) {
1927        return TCL_ERROR;
1928    }
1929    NanoVis::renderContext->setShadingModel(model);
1930    NanoVis::eventuallyRedraw();
1931    return TCL_OK;
1932}
1933
1934static int
1935HeightMapTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1936                     Tcl_Obj *const *objv)
1937{
1938    const char *name;
1939    name = Tcl_GetString(objv[2]);
1940    TransferFunction *tf = NanoVis::getTransferFunction(name);
1941    if (tf == NULL) {
1942        Tcl_AppendResult(interp, "transfer function \"", name,
1943                         "\" is not defined", (char*)NULL);
1944        return TCL_ERROR;
1945    }
1946    std::vector<HeightMap *> imap;
1947    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
1948        return TCL_ERROR;
1949    }
1950    std::vector<HeightMap *>::iterator iter;
1951    for (iter = imap.begin(); iter != imap.end(); iter++) {
1952        (*iter)->transferFunction(tf);
1953    }
1954    NanoVis::eventuallyRedraw();
1955    return TCL_OK;
1956}
1957
1958
1959static int
1960HeightMapOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1961                   Tcl_Obj *const *objv)
1962{
1963    float opacity;
1964    if (GetFloatFromObj(interp, objv[2], &opacity) != TCL_OK) {
1965        return TCL_ERROR;
1966    }
1967    std::vector<HeightMap *> heightmaps;
1968    if (GetHeightMaps(interp, objc - 3, objv + 3, &heightmaps) != TCL_OK) {
1969        return TCL_ERROR;
1970    }
1971    std::vector<HeightMap *>::iterator iter;
1972    for (iter = heightmaps.begin(); iter != heightmaps.end(); iter++) {
1973        (*iter)->opacity(opacity);
1974    }
1975    NanoVis::eventuallyRedraw();
1976    return TCL_OK;
1977}
1978
1979static Rappture::CmdSpec heightMapOps[] = {
1980    {"create",       2, HeightMapCreateOp,      10, 10, "tag xmin ymin xmax ymax xnum ynum values",},
1981    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
1982    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
1983    {"legend",       2, HeightMapLegendOp,      5, 5, "index width height",},
1984    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
1985    {"opacity",      1, HeightMapOpacityOp,     3, 0, "value ?heightmap...? ",},
1986    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
1987    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
1988    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?heightmap...?",},
1989};
1990static int nHeightMapOps = NumCmdSpecs(heightMapOps);
1991
1992static int
1993HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
1994             Tcl_Obj *const *objv)
1995{
1996    Tcl_ObjCmdProc *proc;
1997
1998    proc = Rappture::GetOpFromObj(interp, nHeightMapOps, heightMapOps,
1999                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2000    if (proc == NULL) {
2001        return TCL_ERROR;
2002    }
2003    return (*proc) (clientData, interp, objc, objv);
2004}
2005
2006static int
2007GridAxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2008                Tcl_Obj *const *objv)
2009{
2010    float r, g, b, a;
2011    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2012        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2013        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2014        return TCL_ERROR;
2015    }
2016    a = 1.0f;
2017    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2018        return TCL_ERROR;
2019    }
2020    if (NanoVis::grid) {
2021        NanoVis::grid->setAxisColor(r, g, b, a);
2022    }
2023    return TCL_OK;
2024}
2025
2026static int
2027GridAxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2028               Tcl_Obj *const *objv)
2029{
2030    int axis;
2031    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
2032        return TCL_ERROR;
2033    }
2034    if (NanoVis::grid != NULL) {
2035        Axis *axisPtr;
2036
2037        axisPtr = NULL;     /* Suppress compiler warning. */
2038        switch (axis) {
2039        case 0: axisPtr = &NanoVis::grid->xAxis; break;
2040        case 1: axisPtr = &NanoVis::grid->yAxis; break;
2041        case 2: axisPtr = &NanoVis::grid->zAxis; break;
2042        }
2043        axisPtr->name(Tcl_GetString(objv[3]));
2044        axisPtr->units(Tcl_GetString(objv[4]));
2045    }
2046    return TCL_OK;
2047}
2048
2049static int
2050GridLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2051                Tcl_Obj *const *objv)
2052{
2053    float r, g, b, a;
2054    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2055        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2056        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2057        return TCL_ERROR;
2058    }
2059    a = 1.0f;
2060    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2061        return TCL_ERROR;
2062    }
2063    if (NanoVis::grid) {
2064        NanoVis::grid->setLineColor(r, g, b, a);
2065    }
2066    return TCL_OK;
2067}
2068
2069static int
2070GridVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2071              Tcl_Obj *const *objv)
2072{
2073    bool visible;
2074    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2075        return TCL_ERROR;
2076    }
2077    NanoVis::grid->setVisible(visible);
2078    return TCL_OK;
2079}
2080
2081static Rappture::CmdSpec gridOps[] = {
2082    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2083    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2084    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2085    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2086};
2087static int nGridOps = NumCmdSpecs(gridOps);
2088
2089static int
2090GridCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2091        Tcl_Obj *const *objv)
2092{
2093    Tcl_ObjCmdProc *proc;
2094
2095    proc = Rappture::GetOpFromObj(interp, nGridOps, gridOps,
2096                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2097    if (proc == NULL) {
2098        return TCL_ERROR;
2099    }
2100    return (*proc) (clientData, interp, objc, objv);
2101}
2102
2103static int
2104AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2105        Tcl_Obj *const *objv)
2106{
2107    if (objc < 2) {
2108        Tcl_AppendResult(interp, "wrong # args: should be \"",
2109                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2110        return TCL_ERROR;
2111    }
2112    const char *string = Tcl_GetString(objv[1]);
2113    char c = string[0];
2114    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2115        bool visible;
2116
2117        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2118            return TCL_ERROR;
2119        }
2120        NanoVis::axisOn = visible;
2121    } else {
2122        Tcl_AppendResult(interp, "bad axis option \"", string,
2123                         "\": should be visible", (char*)NULL);
2124        return TCL_ERROR;
2125    }
2126    return TCL_OK;
2127}
2128
2129/*
2130 * This command should be Tcl procedure instead of a C command.  The reason
2131 * for this that 1) we are using a safe interpreter so we would need a master
2132 * interpreter to load the Tcl environment properly (including our "unirect2d"
2133 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2134 * easy to add new directories for procedures, since it's loaded into /tmp.
2135 *
2136 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2137 * to verify the structure and then pass it to the appropiate Tcl command
2138 * (heightmap, volume, etc). Our C command always creates a heightmap.
2139 */
2140static int
2141Unirect2dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2142             Tcl_Obj *const *objv)
2143{
2144    Rappture::Unirect2d *dataPtr = (Rappture::Unirect2d *)clientData;
2145
2146    return dataPtr->loadData(interp, objc, objv);
2147}
2148
2149/*
2150 * This command should be Tcl procedure instead of a C command.  The reason
2151 * for this that 1) we are using a safe interpreter so we would need a master
2152 * interpreter to load the Tcl environment properly (including our "unirect2d"
2153 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2154 * easy to add new directories for procedures, since it's loaded into /tmp.
2155 *
2156 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2157 * to verify the structure and then pass it to the appropiate Tcl command
2158 * (heightmap, volume, etc). Our C command always creates a heightmap.
2159 */
2160
2161static int
2162Unirect3dCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2163             Tcl_Obj *const *objv)
2164{
2165    Rappture::Unirect3d *dataPtr = (Rappture::Unirect3d *)clientData;
2166
2167    return dataPtr->loadData(interp, objc, objv);
2168}
2169
2170Tcl_Interp *
2171initTcl()
2172{
2173    /*
2174     * Ideally the connection is authenticated by nanoscale.  I still like the
2175     * idea of creating a non-safe master interpreter with a safe slave
2176     * interpreter.  Alias all the nanovis commands in the slave. That way we
2177     * can still run Tcl code within nanovis.  The eventual goal is to create
2178     * a test harness through the interpreter for nanovis.
2179     */
2180    Tcl_Interp *interp;
2181    interp = Tcl_CreateInterp();
2182    /*
2183    Tcl_MakeSafe(interp);
2184    */
2185    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
2186    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
2187    Tcl_CreateObjCommand(interp, "clientinfo",  ClientInfoCmd,  NULL, NULL);
2188    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
2189    if (FlowCmdInitProc(interp) != TCL_OK) {
2190        return NULL;
2191    }
2192    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
2193    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
2194    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
2195    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
2196    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    NULL, NULL);
2197    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   NULL, NULL);
2198    Tcl_CreateObjCommand(interp, "unirect2d",   Unirect2dCmd,   NULL, NULL);
2199    Tcl_CreateObjCommand(interp, "unirect3d",   Unirect3dCmd,   NULL, NULL);
2200    Tcl_CreateObjCommand(interp, "up",          UpCmd,          NULL, NULL);
2201    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
2202
2203    // create a default transfer function
2204    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2205        WARN("bad default transfer function:\n%s", 
2206             Tcl_GetStringResult(interp));
2207    }
2208    return interp;
2209}
Note: See TracBrowser for help on using the repository browser.