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

Last change on this file since 4599 was 3568, checked in by ldelgass, 7 years ago

Fix crash on getting transfer function name, remove unused plane command.

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