source: nanovis/tags/1.1.3/Command.cpp @ 4988

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

Preallocate full size Rappture buffer for data payloads

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