source: nanovis/trunk/Command.cpp @ 4923

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

Sync with release branch

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