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

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

Add basic VTK structured points reader to nanovis, update copyright dates.

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