source: trunk/packages/vizservers/nanovis/Command.cpp @ 3478

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

Add background color protocol command to nanovis, also change resize protocol
to use 'size', so new commands are:

screen bgcolor r g b
screen size width height

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