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

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

Merge r3611:3618 from trunk

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