source: trunk/packages/vizservers/nanovis/Command.cpp @ 4067

Last change on this file since 4067 was 4067, checked in by ldelgass, 9 years ago

Use TransferFunction::sample() instead of Rappture::Field1D

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