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

Last change on this file since 4879 was 4879, checked in by ldelgass, 6 years ago

Fix for ExecuteCommand?

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