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

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

fix for snapshot command

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