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

Last change on this file since 4888 was 4881, checked in by ldelgass, 10 years ago

Fixes for video generation

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