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

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

Begin process of renaming R2 library

  • Property svn:eol-style set to native
File size: 72.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-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
887/*
888 * ----------------------------------------------------------------------
889 * CLIENT COMMAND:
890 *   screen <width> <height>
891 *
892 * Clients send this command to set the size of the rendering area.
893 * Future images are generated at the specified width/height.
894 * ----------------------------------------------------------------------
895 */
896static int
897ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
898          Tcl_Obj *const *objv)
899{
900    if (objc != 3) {
901        Tcl_AppendResult(interp, "wrong # args: should be \"",
902                         Tcl_GetString(objv[0]), " width height\"", (char*)NULL);
903        return TCL_ERROR;
904    }
905
906    int w, h;
907    if ((Tcl_GetIntFromObj(interp, objv[1], &w) != TCL_OK) ||
908        (Tcl_GetIntFromObj(interp, objv[2], &h) != TCL_OK)) {
909        return TCL_ERROR;
910    }
911    NanoVis::resizeOffscreenBuffer(w, h);
912    return TCL_OK;
913}
914
915/*
916 * ----------------------------------------------------------------------
917 * CLIENT COMMAND:
918 *   transfunc define <name> <colormap> <alphamap>
919 *     where <colormap> = { <v> <r> <g> <b> ... }
920 *           <alphamap> = { <v> <w> ... }
921 *
922 * Clients send these commands to manipulate the transfer functions.
923 * ----------------------------------------------------------------------
924 */
925static int
926TransfuncCmd(ClientData clientData, Tcl_Interp *interp, int objc,
927             Tcl_Obj *const *objv)
928{
929    if (objc < 2) {
930        Tcl_AppendResult(interp, "wrong # args: should be \"",
931                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
932        return TCL_ERROR;
933    }
934
935    const char *string = Tcl_GetString(objv[1]);
936    char c = string[0];
937    if ((c == 'd') && (strcmp(string, "define") == 0)) {
938        if (objc != 5) {
939            Tcl_AppendResult(interp, "wrong # args: should be \"",
940                Tcl_GetString(objv[0]), " define name colorMap alphaMap\"",
941                (char*)NULL);
942            return TCL_ERROR;
943        }
944
945        // decode the data and store in a series of fields
946        Rappture::Field1D rFunc, gFunc, bFunc, wFunc;
947        int cmapc, wmapc, i;
948        Tcl_Obj **cmapv;
949        Tcl_Obj **wmapv;
950
951        wmapv = cmapv = NULL;
952        if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
953            return TCL_ERROR;
954        }
955        if ((cmapc % 4) != 0) {
956            Tcl_AppendResult(interp, "wrong # elements is colormap: should be ",
957                "{ v r g b ... }", (char*)NULL);
958            return TCL_ERROR;
959        }
960        if (Tcl_ListObjGetElements(interp, objv[4], &wmapc, &wmapv) != TCL_OK) {
961            return TCL_ERROR;
962        }
963        if ((wmapc % 2) != 0) {
964            Tcl_AppendResult(interp, "wrong # elements in alphamap: should be ",
965                " { v w ... }", (char*)NULL);
966            return TCL_ERROR;
967        }
968        for (i = 0; i < cmapc; i += 4) {
969            int j;
970            double q[4];
971
972            for (j=0; j < 4; j++) {
973                if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &q[j]) != TCL_OK) {
974                    return TCL_ERROR;
975                }
976                if ((q[j] < 0.0) || (q[j] > 1.0)) {
977                    Tcl_AppendResult(interp, "bad colormap value \"",
978                        Tcl_GetString(cmapv[i+j]),
979                        "\": should be in the range 0-1", (char*)NULL);
980                    return TCL_ERROR;
981                }
982            }
983            rFunc.define(q[0], q[1]);
984            gFunc.define(q[0], q[2]);
985            bFunc.define(q[0], q[3]);
986        }
987        for (i=0; i < wmapc; i += 2) {
988            double q[2];
989            int j;
990
991            for (j=0; j < 2; j++) {
992                if (Tcl_GetDoubleFromObj(interp, wmapv[i+j], &q[j]) != TCL_OK) {
993                    return TCL_ERROR;
994                }
995                if ((q[j] < 0.0) || (q[j] > 1.0)) {
996                    Tcl_AppendResult(interp, "bad alphamap value \"",
997                        Tcl_GetString(wmapv[i+j]),
998                        "\": should be in the range 0-1", (char*)NULL);
999                    return TCL_ERROR;
1000                }
1001            }
1002            wFunc.define(q[0], q[1]);
1003        }
1004        // sample the given function into discrete slots
1005        const int nslots = 256;
1006        float data[4*nslots];
1007        for (i=0; i < nslots; i++) {
1008            double x = double(i)/(nslots-1);
1009            data[4*i]   = rFunc.value(x);
1010            data[4*i+1] = gFunc.value(x);
1011            data[4*i+2] = bFunc.value(x);
1012            data[4*i+3] = wFunc.value(x);
1013        }
1014        // find or create this transfer function
1015        NanoVis::defineTransferFunction(Tcl_GetString(objv[2]), nslots, data);
1016    } else {
1017        Tcl_AppendResult(interp, "bad option \"", string,
1018                "\": should be define", (char*)NULL);
1019        return TCL_ERROR;
1020    }
1021    return TCL_OK;
1022}
1023
1024/*
1025 * ----------------------------------------------------------------------
1026 * CLIENT COMMAND:
1027 *   up axis
1028 *
1029 * Clients use this to set the "up" direction for all volumes.  Volumes
1030 * are oriented such that this direction points upward.
1031 * ----------------------------------------------------------------------
1032 */
1033static int
1034UpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1035{
1036    if (objc != 2) {
1037        Tcl_AppendResult(interp, "wrong # args: should be \"",
1038                         Tcl_GetString(objv[0]), " x|y|z|-x|-y|-z\"", (char*)NULL);
1039        return TCL_ERROR;
1040    }
1041
1042    int sign;
1043    int axis;
1044    if (GetAxisDirFromObj(interp, objv[1], &axis, &sign) != TCL_OK) {
1045        return TCL_ERROR;
1046    }
1047    NanoVis::updir = (axis+1)*sign;
1048    return TCL_OK;
1049}
1050
1051static int
1052VolumeAnimationCaptureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1053                         Tcl_Obj *const *objv)
1054{
1055    int total;
1056    if (Tcl_GetIntFromObj(interp, objv[3], &total) != TCL_OK) {
1057        return TCL_ERROR;
1058    }
1059    VolumeInterpolator* interpolator;
1060    interpolator = NanoVis::volRenderer->getVolumeInterpolator();
1061    interpolator->start();
1062    if (interpolator->isStarted()) {
1063        const char *fileName = (objc < 5) ? NULL : Tcl_GetString(objv[4]);
1064        for (int frame_num = 0; frame_num < total; ++frame_num) {
1065            float fraction;
1066
1067            fraction = ((float)frame_num) / (total - 1);
1068            TRACE("fraction : %f", fraction);
1069            //interpolator->update(((float)frame_num) / (total - 1));
1070            interpolator->update(fraction);
1071
1072            int fboOrig;
1073            glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboOrig);
1074
1075            NanoVis::bindOffscreenBuffer();  //enable offscreen render
1076
1077            NanoVis::display();
1078            NanoVis::readScreen();
1079
1080            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboOrig);
1081
1082            NanoVis::bmpWriteToFile(frame_num, fileName);
1083        }
1084    }
1085    return TCL_OK;
1086}
1087
1088static int
1089VolumeAnimationClearOp(ClientData clientData, Tcl_Interp *interp, int objc,
1090                       Tcl_Obj *const *objv)
1091{
1092    NanoVis::volRenderer->clearAnimatedVolumeInfo();
1093    return TCL_OK;
1094}
1095
1096static int
1097VolumeAnimationStartOp(ClientData clientData, Tcl_Interp *interp, int objc,
1098                       Tcl_Obj *const *objv)
1099{
1100    NanoVis::volRenderer->startVolumeAnimation();
1101    return TCL_OK;
1102}
1103
1104static int
1105VolumeAnimationStopOp(ClientData clientData, Tcl_Interp *interp, int objc,
1106                      Tcl_Obj *const *objv)
1107{
1108    NanoVis::volRenderer->stopVolumeAnimation();
1109    return TCL_OK;
1110}
1111
1112static int
1113VolumeAnimationVolumesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1114                         Tcl_Obj *const *objv)
1115{
1116    std::vector<Volume *> volumes;
1117    if (GetVolumes(interp, objc - 3, objv + 3, &volumes) != TCL_OK) {
1118        return TCL_ERROR;
1119    }
1120    TRACE("parsing volume data identifier");
1121    Tcl_HashSearch iter;
1122    Tcl_HashEntry *hPtr;
1123    for (hPtr = Tcl_FirstHashEntry(&NanoVis::volumeTable, &iter); hPtr != NULL;
1124         hPtr = Tcl_NextHashEntry(&iter)) {
1125        Volume *volPtr;
1126        volPtr = (Volume *)Tcl_GetHashValue(hPtr);
1127        NanoVis::volRenderer->addAnimatedVolume(volPtr);
1128    }
1129    return TCL_OK;
1130}
1131
1132static Rappture::CmdSpec volumeAnimationOps[] = {
1133    {"capture",   2, VolumeAnimationCaptureOp,  4, 5, "numframes ?filename?",},
1134    {"clear",     2, VolumeAnimationClearOp,    3, 3, "",},
1135    {"start",     3, VolumeAnimationStartOp,    3, 3, "",},
1136    {"stop",      3, VolumeAnimationStopOp,     3, 3, "",},
1137    {"volumes",   1, VolumeAnimationVolumesOp,  3, 0, "?indices?",},
1138};
1139
1140static int nVolumeAnimationOps = NumCmdSpecs(volumeAnimationOps);
1141
1142static int
1143VolumeAnimationOp(ClientData clientData, Tcl_Interp *interp, int objc,
1144                  Tcl_Obj *const *objv)
1145{
1146    Tcl_ObjCmdProc *proc;
1147
1148    proc = Rappture::GetOpFromObj(interp, nVolumeAnimationOps, 
1149                volumeAnimationOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1150    if (proc == NULL) {
1151        return TCL_ERROR;
1152    }
1153    return (*proc) (clientData, interp, objc, objv);
1154}
1155
1156static int
1157VolumeDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1158                    Tcl_Obj *const *objv)
1159{
1160    TRACE("Data Loading");
1161
1162    int nbytes;
1163    if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
1164        return TCL_ERROR;
1165    }
1166    const char *tag;
1167    tag = Tcl_GetString(objv[4]);
1168    Rappture::Buffer buf;
1169    if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
1170        return TCL_ERROR;
1171    }
1172    const char *bytes;
1173    size_t nBytes;
1174
1175    bytes = buf.bytes();
1176    nBytes = buf.size();
1177
1178    TRACE("Checking header[%.20s]", bytes);
1179
1180    Volume *volPtr = NULL;
1181
1182    if ((nBytes > 5) && (strncmp(bytes, "<HDR>", 5) == 0)) {
1183        TRACE("ZincBlende stream is in");
1184         //std::stringstream fdata(std::ios_base::out|std::ios_base::in|std::ios_base::binary);
1185        //fdata.write(buf.bytes(),buf.size());
1186        //vol = NvZincBlendeReconstructor::getInstance()->loadFromStream(fdata);
1187
1188        volPtr = NvZincBlendeReconstructor::getInstance()->loadFromMemory((void*) buf.bytes());
1189        if (volPtr == NULL) {
1190            Tcl_AppendResult(interp, "can't get volume instance", (char *)NULL);
1191            return TCL_ERROR;
1192        }
1193        TRACE("finish loading");
1194
1195        Vector3 scale = volPtr->getPhysicalScaling();
1196        float dx0 = -0.5 * scale.x;
1197        float dy0 = -0.5 * scale.y;
1198        float dz0 = -0.5 * scale.z;
1199        volPtr->location(Vector3(dx0, dy0, dz0));
1200
1201        int isNew;
1202        Tcl_HashEntry *hPtr;
1203        hPtr = Tcl_CreateHashEntry(&NanoVis::volumeTable, tag, &isNew);
1204        if (!isNew) {
1205            Tcl_AppendResult(interp, "volume \"", tag, "\" already exists.",
1206                             (char *)NULL);
1207            return TCL_ERROR;
1208        }
1209        Tcl_SetHashValue(hPtr, volPtr);
1210        volPtr->name(Tcl_GetHashKey(&NanoVis::volumeTable, hPtr));
1211    } else {
1212        if ((nBytes > 5) && (strncmp(bytes, "<ODX>", 5) == 0)) {
1213            bytes += 5;
1214            nBytes -= 5;
1215        }
1216        TRACE("DX loading...");
1217        std::stringstream fdata;
1218        fdata.write(bytes, nBytes);
1219        if (nBytes <= 0) {
1220            ERROR("data buffer is empty");
1221            abort();
1222        }
1223        Rappture::Outcome context;
1224        volPtr = load_volume_stream(context, tag, fdata);
1225        if (volPtr == NULL) {
1226            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1227            return TCL_ERROR;
1228        }
1229    }
1230
1231    //
1232    // BE CAREFUL: Set the number of slices to something slightly different
1233    // for each volume.  If we have identical volumes at exactly the same
1234    // position with exactly the same number of slices, the second volume will
1235    // overwrite the first, so the first won't appear at all.
1236    //
1237    if (volPtr != NULL) {
1238        //volPtr->numSlices(512-n);
1239        //volPtr->numSlices(256-n);
1240        volPtr->disableCutplane(0);
1241        volPtr->disableCutplane(1);
1242        volPtr->disableCutplane(2);
1243        volPtr->transferFunction(NanoVis::getTransfunc("default"));
1244        volPtr->visible(true);
1245
1246        char info[1024];
1247        ssize_t nWritten;
1248
1249        if (Volume::updatePending) {
1250            NanoVis::setVolumeRanges();
1251        }
1252
1253        // FIXME: strlen(info) is the return value of sprintf
1254        sprintf(info, "nv>data tag %s min %g max %g vmin %g vmax %g\n", tag, 
1255                volPtr->wAxis.min(), volPtr->wAxis.max(),
1256                Volume::valueMin, Volume::valueMax);
1257        nWritten  = write(1, info, strlen(info));
1258        assert(nWritten == (ssize_t)strlen(info));
1259    }
1260    return TCL_OK;
1261}
1262
1263static int
1264VolumeDataStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1265                  Tcl_Obj *const *objv)
1266{
1267    bool state;
1268    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1269        return TCL_ERROR;
1270    }
1271    std::vector<Volume *> ivol;
1272    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1273        return TCL_ERROR;
1274    }
1275    std::vector<Volume *>::iterator iter;
1276    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1277        (*iter)->dataEnabled(state);
1278    }
1279    return TCL_OK;
1280}
1281
1282static Rappture::CmdSpec volumeDataOps[] = {
1283    {"follows",   1, VolumeDataFollowsOp, 5, 5, "size tag",},
1284    {"state",     1, VolumeDataStateOp,   4, 0, "bool ?indices?",},
1285};
1286static int nVolumeDataOps = NumCmdSpecs(volumeDataOps);
1287
1288static int
1289VolumeDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1290             Tcl_Obj *const *objv)
1291{
1292    Tcl_ObjCmdProc *proc;
1293
1294    proc = Rappture::GetOpFromObj(interp, nVolumeDataOps, volumeDataOps,
1295                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1296    if (proc == NULL) {
1297        return TCL_ERROR;
1298    }
1299    return (*proc) (clientData, interp, objc, objv);
1300}
1301
1302static int
1303VolumeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1304               Tcl_Obj *const *objv)
1305{
1306    int i;
1307
1308    for (i = 2; i < objc; i++) {
1309        Volume *volPtr;
1310
1311        if (GetVolumeFromObj(interp, objv[i], &volPtr) != TCL_OK) {
1312            return TCL_ERROR;
1313        }
1314        NanoVis::removeVolume(volPtr);
1315    }
1316    NanoVis::eventuallyRedraw();
1317    return TCL_OK;
1318}
1319
1320static int
1321VolumeExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1322               Tcl_Obj *const *objv)
1323{
1324    bool value;
1325    Volume *volPtr;
1326
1327    value = false;
1328    if (GetVolumeFromObj(NULL, objv[2], &volPtr) == TCL_OK) {
1329        value = true;
1330    }
1331    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
1332    return TCL_OK;
1333}
1334
1335static int
1336VolumeNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1337              Tcl_Obj *const *objv)
1338{
1339    Tcl_Obj *listObjPtr;
1340    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1341    Tcl_HashEntry *hPtr; 
1342    Tcl_HashSearch iter;
1343    for (hPtr = Tcl_FirstHashEntry(&NanoVis::volumeTable, &iter); hPtr != NULL; 
1344         hPtr = Tcl_NextHashEntry(&iter)) {
1345        Volume *volPtr;
1346        volPtr = (Volume *)Tcl_GetHashValue(hPtr);
1347        Tcl_Obj *objPtr;
1348        objPtr = Tcl_NewStringObj(volPtr->name(), -1);
1349        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1350    }
1351    Tcl_SetObjResult(interp, listObjPtr);
1352    return TCL_OK;
1353}
1354
1355static int
1356VolumeOutlineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1357                     Tcl_Obj *const *objv)
1358{
1359    float rgb[3];
1360    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1361        return TCL_ERROR;
1362    }
1363    std::vector<Volume *> ivol;
1364    if (GetVolumes(interp, objc - 6, objv + 6, &ivol) != TCL_OK) {
1365        return TCL_ERROR;
1366    }
1367    std::vector<Volume *>::iterator iter;
1368    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1369        (*iter)->setOutlineColor(rgb);
1370    }
1371    return TCL_OK;
1372}
1373
1374static int
1375VolumeOutlineStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1376                     Tcl_Obj *const *objv)
1377{
1378    bool state;
1379    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1380        return TCL_ERROR;
1381    }
1382    std::vector<Volume *> ivol;
1383    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1384        return TCL_ERROR;
1385    }
1386    std::vector<Volume *>::iterator iter;
1387    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1388        (*iter)->outline(state);
1389    }
1390    return TCL_OK;
1391}
1392
1393
1394static Rappture::CmdSpec volumeOutlineOps[] = {
1395    {"color",     1, VolumeOutlineColorOp,  6, 0, "r g b ?indices?",},
1396    {"state",     1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1397    {"visible",   1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1398};
1399static int nVolumeOutlineOps = NumCmdSpecs(volumeOutlineOps);
1400
1401static int
1402VolumeOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc,
1403                Tcl_Obj *const *objv)
1404{
1405    Tcl_ObjCmdProc *proc;
1406
1407    proc = Rappture::GetOpFromObj(interp, nVolumeOutlineOps, volumeOutlineOps,
1408        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1409    if (proc == NULL) {
1410        return TCL_ERROR;
1411    }
1412    return (*proc) (clientData, interp, objc, objv);
1413}
1414
1415static int
1416VolumeShadingAmbientOp(ClientData clientData, Tcl_Interp *interp, int objc,
1417                       Tcl_Obj *const *objv)
1418{
1419    float ambient;
1420    if (GetFloatFromObj(interp, objv[3], &ambient) != TCL_OK) {
1421        return TCL_ERROR;
1422    }
1423    if (ambient < 0.0f || ambient > 1.0f) {
1424        WARN("Invalid ambient coefficient requested: %g, should be [0,1]", ambient);
1425    }
1426    std::vector<Volume *> ivol;
1427    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1428        return TCL_ERROR;
1429    }
1430    std::vector<Volume *>::iterator iter;
1431    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1432        (*iter)->ambient(ambient);
1433    }
1434    return TCL_OK;
1435}
1436
1437static int
1438VolumeShadingDiffuseOp(ClientData clientData, Tcl_Interp *interp, int objc,
1439                       Tcl_Obj *const *objv)
1440{
1441    float diffuse;
1442    if (GetFloatFromObj(interp, objv[3], &diffuse) != TCL_OK) {
1443        return TCL_ERROR;
1444    }
1445    if (diffuse < 0.0f || diffuse > 1.0f) {
1446        WARN("Invalid diffuse coefficient requested: %g, should be [0,1]", diffuse);
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)->diffuse(diffuse);
1455    }
1456    return TCL_OK;
1457}
1458
1459static int
1460VolumeShadingIsosurfaceOp(ClientData clientData, Tcl_Interp *interp, int objc,
1461                          Tcl_Obj *const *objv)
1462{
1463    bool iso_surface;
1464    if (GetBooleanFromObj(interp, objv[3], &iso_surface) != TCL_OK) {
1465        return TCL_ERROR;
1466    }
1467    std::vector<Volume *> ivol;
1468    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1469        return TCL_ERROR;
1470    }
1471    std::vector<Volume *>::iterator iter;
1472    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1473        (*iter)->isosurface(iso_surface);
1474    }
1475    return TCL_OK;
1476}
1477
1478static int
1479VolumeShadingLight2SideOp(ClientData clientData, Tcl_Interp *interp, int objc,
1480                          Tcl_Obj *const *objv)
1481{
1482    bool twoSidedLighting;
1483    if (GetBooleanFromObj(interp, objv[3], &twoSidedLighting) != TCL_OK) {
1484        return TCL_ERROR;
1485    }
1486    std::vector<Volume *> ivol;
1487    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1488        return TCL_ERROR;
1489    }
1490    std::vector<Volume *>::iterator iter;
1491    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1492        (*iter)->twoSidedLighting(twoSidedLighting);
1493    }
1494    return TCL_OK;
1495}
1496
1497static int
1498VolumeShadingOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1499                       Tcl_Obj *const *objv)
1500{
1501
1502    float opacity;
1503    if (GetFloatFromObj(interp, objv[3], &opacity) != TCL_OK) {
1504        return TCL_ERROR;
1505    }
1506    TRACE("set opacity %f", opacity);
1507    std::vector<Volume *> ivol;
1508    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1509        return TCL_ERROR;
1510    }
1511    std::vector<Volume *>::iterator iter;
1512    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1513        (*iter)->opacityScale(opacity);
1514    }
1515    return TCL_OK;
1516}
1517
1518static int
1519VolumeShadingSpecularOp(ClientData clientData, Tcl_Interp *interp, int objc,
1520                        Tcl_Obj *const *objv)
1521{
1522    float specular;
1523    if (GetFloatFromObj(interp, objv[3], &specular) != TCL_OK) {
1524        return TCL_ERROR;
1525    }
1526    if (specular < 0.0f || specular > 1.0f) {
1527        WARN("Invalid specular coefficient requested: %g, should be [0,1]", specular);
1528    }
1529    std::vector<Volume *> ivol;
1530    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1531        return TCL_ERROR;
1532    }
1533    std::vector<Volume *>::iterator iter;
1534    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1535        (*iter)->specularLevel(specular);
1536    }
1537    return TCL_OK;
1538}
1539
1540static int
1541VolumeShadingSpecularExpOp(ClientData clientData, Tcl_Interp *interp, int objc,
1542                           Tcl_Obj *const *objv)
1543{
1544    float specularExp;
1545    if (GetFloatFromObj(interp, objv[3], &specularExp) != TCL_OK) {
1546        return TCL_ERROR;
1547    }
1548    if (specularExp < 0.0f || specularExp > 128.0f) {
1549        WARN("Invalid specular exponent requested: %g, should be [0,128]", specularExp);
1550    }
1551    std::vector<Volume *> ivol;
1552    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1553        return TCL_ERROR;
1554    }
1555    std::vector<Volume *>::iterator iter;
1556    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1557        (*iter)->specularExponent(specularExp);
1558    }
1559    return TCL_OK;
1560}
1561
1562static int
1563VolumeShadingTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1564                         Tcl_Obj *const *objv)
1565{
1566    TransferFunction *tfPtr;
1567    const char *name = Tcl_GetString(objv[3]);
1568    tfPtr = NanoVis::getTransfunc(name);
1569    if (tfPtr == NULL) {
1570        Tcl_AppendResult(interp, "transfer function \"", name,
1571                         "\" is not defined", (char*)NULL);
1572        return TCL_ERROR;
1573    }
1574    std::vector<Volume *> ivol;
1575    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1576        return TCL_ERROR;
1577    }
1578    std::vector<Volume *>::iterator iter;
1579    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1580        TRACE("setting %s with transfer function %s", (*iter)->name(),
1581               tfPtr->name());
1582        (*iter)->transferFunction(tfPtr);
1583#ifdef USE_POINTSET_RENDERER
1584        // TBD..
1585        if ((*iter)->pointsetIndex != -1) {
1586            NanoVis::pointSet[(*iter)->pointsetIndex]->updateColor(tfPtr->getData(), 256);
1587        }
1588#endif
1589    }
1590    return TCL_OK;
1591}
1592
1593static Rappture::CmdSpec volumeShadingOps[] = {
1594    {"ambient",       1, VolumeShadingAmbientOp,     4, 0, "value ?indices?",},
1595    {"diffuse",       1, VolumeShadingDiffuseOp,     4, 0, "value ?indices?",},
1596    {"isosurface",    1, VolumeShadingIsosurfaceOp,  4, 0, "bool ?indices?",},
1597    {"light2side",    1, VolumeShadingLight2SideOp,  4, 0, "bool ?indices?",},
1598    {"opacity",       1, VolumeShadingOpacityOp,     4, 0, "value ?indices?",},
1599    {"specularExp",   9, VolumeShadingSpecularExpOp, 4, 0, "value ?indices?",},
1600    {"specularLevel", 9, VolumeShadingSpecularOp,    4, 0, "value ?indices?",},
1601    {"transfunc",     1, VolumeShadingTransFuncOp,   4, 0, "funcName ?indices?",},
1602};
1603static int nVolumeShadingOps = NumCmdSpecs(volumeShadingOps);
1604
1605static int
1606VolumeShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1607                Tcl_Obj *const *objv)
1608{
1609    Tcl_ObjCmdProc *proc;
1610
1611    proc = Rappture::GetOpFromObj(interp, nVolumeShadingOps, volumeShadingOps,
1612        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1613    if (proc == NULL) {
1614        return TCL_ERROR;
1615    }
1616    return (*proc) (clientData, interp, objc, objv);
1617}
1618
1619static int
1620VolumeStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1621              Tcl_Obj *const *objv)
1622{
1623    bool state;
1624    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1625        return TCL_ERROR;
1626    }
1627    std::vector<Volume *> ivol;
1628    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
1629        return TCL_ERROR;
1630    }
1631    std::vector<Volume *>::iterator iter;
1632    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1633        (*iter)->visible(state);
1634    }
1635    return TCL_OK;
1636}
1637
1638static int
1639VolumeTestOp(ClientData clientData, Tcl_Interp *interp, int objc,
1640             Tcl_Obj *const *objv)
1641{
1642    // Find the first volume in the vector.
1643    Tcl_HashEntry *hPtr;
1644    Tcl_HashSearch iter;
1645    hPtr = Tcl_FirstHashEntry(&NanoVis::volumeTable, &iter); 
1646    if (hPtr != NULL) {
1647        Volume *volPtr;
1648        volPtr = (Volume *)Tcl_GetHashValue(hPtr);
1649        volPtr->dataEnabled(false);
1650        volPtr->visible(false);
1651    }
1652    return TCL_OK;
1653}
1654
1655static Rappture::CmdSpec volumeOps[] = {
1656    {"animation", 2, VolumeAnimationOp,   3, 0, "oper ?args?",},
1657    {"data",      2, VolumeDataOp,        3, 0, "oper ?args?",},
1658    {"delete",    2, VolumeDeleteOp,      3, 0, "?name...?",},
1659    {"exists",    1, VolumeExistsOp,      3, 3, "name",},
1660    {"names",     1, VolumeNamesOp,       2, 3, "?pattern?",},
1661    {"outline",   1, VolumeOutlineOp,     3, 0, "oper ?args?",},
1662    {"shading",   2, VolumeShadingOp,     3, 0, "oper ?args?",},
1663    {"state",     2, VolumeStateOp,       3, 0, "bool ?indices?",},
1664    {"test2",     1, VolumeTestOp,        2, 2, "",},
1665};
1666static int nVolumeOps = NumCmdSpecs(volumeOps);
1667
1668/*
1669 * ----------------------------------------------------------------------
1670 * CLIENT COMMAND:
1671 *   volume data state on|off ?<volumeId> ...?
1672 *   volume outline state on|off ?<volumeId> ...?
1673 *   volume outline color on|off ?<volumeId> ...?
1674 *   volume shading transfunc <name> ?<volumeId> ...?
1675 *   volume shading diffuse <value> ?<volumeId> ...?
1676 *   volume shading specular <value> ?<volumeId> ...?
1677 *   volume shading opacity <value> ?<volumeId> ...?
1678 *   volume state on|off ?<volumeId> ...?
1679 *
1680 * Clients send these commands to manipulate the volumes.
1681 * ----------------------------------------------------------------------
1682 */
1683static int
1684VolumeCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
1685          Tcl_Obj *const *objv)
1686{
1687    Tcl_ObjCmdProc *proc;
1688
1689    proc = Rappture::GetOpFromObj(interp, nVolumeOps, volumeOps,
1690        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1691    if (proc == NULL) {
1692        return TCL_ERROR;
1693    }
1694    return (*proc) (clientData, interp, objc, objv);
1695}
1696
1697static int
1698HeightMapDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1699                       Tcl_Obj *const *objv)
1700{
1701    int nBytes;
1702    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
1703        return TCL_ERROR;
1704    }
1705    const char *tag;
1706    tag = Tcl_GetString(objv[4]);
1707    int isNew;
1708    Tcl_HashEntry *hPtr;
1709
1710    Rappture::Buffer buf;
1711    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
1712        return TCL_ERROR;
1713    }
1714    Rappture::Unirect2d data(1);
1715    if (data.parseBuffer(interp, buf) != TCL_OK) {
1716        return TCL_ERROR;
1717    }
1718    if (data.nValues() == 0) {
1719        Tcl_AppendResult(interp, "no data found in stream", (char *)NULL);
1720        return TCL_ERROR;
1721    }
1722    if (!data.isInitialized()) {
1723        return TCL_ERROR;
1724    }
1725    HeightMap* hmPtr;
1726    hPtr = Tcl_CreateHashEntry(&NanoVis::heightmapTable, tag, &isNew);
1727    if (isNew) {
1728        hmPtr = new HeightMap();
1729        Tcl_SetHashValue(hPtr, hmPtr);
1730    } else {
1731        hmPtr = (HeightMap *)Tcl_GetHashValue(hPtr);
1732    }
1733    TRACE("Number of heightmaps=%d", NanoVis::heightmapTable.numEntries);
1734    // Must set units before the heights.
1735    hmPtr->xAxis.units(data.xUnits());
1736    hmPtr->yAxis.units(data.yUnits());
1737    hmPtr->zAxis.units(data.vUnits());
1738    hmPtr->wAxis.units(data.yUnits());
1739    hmPtr->setHeight(data.xMin(), data.yMin(), data.xMax(), data.yMax(), 
1740                     data.xNum(), data.yNum(), data.transferValues());
1741    hmPtr->transferFunction(NanoVis::getTransfunc("default"));
1742    hmPtr->setVisible(true);
1743    hmPtr->setLineContourVisible(true);
1744    NanoVis::eventuallyRedraw();
1745    return TCL_OK;
1746}
1747
1748static int
1749HeightMapDataVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1750                       Tcl_Obj *const *objv)
1751{
1752    bool visible;
1753    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1754        return TCL_ERROR;
1755    }
1756    std::vector<HeightMap *> imap;
1757    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1758        return TCL_ERROR;
1759    }
1760    std::vector<HeightMap *>::iterator iter;
1761    for (iter = imap.begin(); iter != imap.end(); iter++) {
1762        (*iter)->setVisible(visible);
1763    }
1764    NanoVis::eventuallyRedraw();
1765    return TCL_OK;
1766}
1767
1768static Rappture::CmdSpec heightMapDataOps[] = {
1769    {"follows",  1, HeightMapDataFollowsOp, 5, 5, "size tag",},
1770    {"visible",  1, HeightMapDataVisibleOp, 4, 0, "bool ?indices?",},
1771};
1772static int nHeightMapDataOps = NumCmdSpecs(heightMapDataOps);
1773
1774static int
1775HeightMapDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1776                Tcl_Obj *const *objv)
1777{
1778    Tcl_ObjCmdProc *proc;
1779
1780    proc = Rappture::GetOpFromObj(interp, nHeightMapDataOps, heightMapDataOps,
1781                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1782    if (proc == NULL) {
1783        return TCL_ERROR;
1784    }
1785    return (*proc) (clientData, interp, objc, objv);
1786}
1787
1788
1789static int
1790HeightMapLineContourColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1791                            Tcl_Obj *const *objv)
1792{
1793    float rgb[3];
1794    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1795        return TCL_ERROR;
1796    }
1797    std::vector<HeightMap *> imap;
1798    if (GetHeightMaps(interp, objc - 6, objv + 6, &imap) != TCL_OK) {
1799        return TCL_ERROR;
1800    }
1801    std::vector<HeightMap *>::iterator iter;
1802    for (iter = imap.begin(); iter != imap.end(); iter++) {
1803        (*iter)->setLineContourColor(rgb);
1804    }
1805    NanoVis::eventuallyRedraw();
1806    return TCL_OK;
1807}
1808
1809static int
1810HeightMapLineContourVisibleOp(ClientData clientData, Tcl_Interp *interp, 
1811                              int objc, Tcl_Obj *const *objv)
1812{
1813    bool visible;
1814    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1815        return TCL_ERROR;
1816    }
1817    std::vector<HeightMap *> imap;
1818    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1819        return TCL_ERROR;
1820    }
1821    std::vector<HeightMap *>::iterator iter;
1822    for (iter = imap.begin(); iter != imap.end(); iter++) {
1823        (*iter)->setLineContourVisible(visible);
1824    }
1825    NanoVis::eventuallyRedraw();
1826    return TCL_OK;
1827}
1828
1829static Rappture::CmdSpec heightMapLineContourOps[] = {
1830    {"color",   1, HeightMapLineContourColorOp,   4, 4, "length",},
1831    {"visible", 1, HeightMapLineContourVisibleOp, 4, 0, "bool ?indices?",},
1832};
1833static int nHeightMapLineContourOps = NumCmdSpecs(heightMapLineContourOps);
1834
1835static int 
1836HeightMapLineContourOp(ClientData clientData, Tcl_Interp *interp, int objc,
1837                       Tcl_Obj *const *objv)
1838{
1839    Tcl_ObjCmdProc *proc;
1840
1841    proc = Rappture::GetOpFromObj(interp, nHeightMapLineContourOps,
1842        heightMapLineContourOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1843    if (proc == NULL) {
1844        return TCL_ERROR;
1845    }
1846    return (*proc) (clientData, interp, objc, objv);
1847}
1848
1849static int
1850HeightMapCullOp(ClientData clientData, Tcl_Interp *interp, int objc,
1851                Tcl_Obj *const *objv)
1852{
1853    RenderContext::CullMode mode;
1854    if (GetCullMode(interp, objv[2], &mode) != TCL_OK) {
1855        return TCL_ERROR;
1856    }
1857    NanoVis::renderContext->setCullMode(mode);
1858    NanoVis::eventuallyRedraw();
1859    return TCL_OK;
1860}
1861
1862static int
1863HeightMapCreateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1864                  Tcl_Obj *const *objv)
1865{
1866    const char *tag;
1867    tag = Tcl_GetString(objv[2]);
1868    Tcl_HashEntry *hPtr;
1869    int isNew;
1870    hPtr = Tcl_CreateHashEntry(&NanoVis::heightmapTable, tag, &isNew);
1871    if (!isNew) {
1872        Tcl_AppendResult(interp, "heightmap \"", tag, "\" already exists.",
1873                         (char *)NULL);
1874        return TCL_ERROR;
1875    }
1876    HeightMap *hmPtr;
1877    /* heightmap create xmin ymin xmax ymax xnum ynum values */
1878    hmPtr = CreateHeightMap(clientData, interp, objc - 3, objv + 3);
1879    if (hmPtr == NULL) {
1880        return TCL_ERROR;
1881    }
1882    Tcl_SetHashValue(hPtr, hmPtr);
1883    Tcl_SetStringObj(Tcl_GetObjResult(interp), tag, -1);;
1884    NanoVis::eventuallyRedraw();
1885    TRACE("Number of heightmaps=%d", NanoVis::heightmapTable.numEntries);
1886    return TCL_OK;
1887}
1888
1889static int
1890HeightMapLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
1891                  Tcl_Obj *const *objv)
1892{
1893    HeightMap *hmPtr;
1894    if (GetHeightMapFromObj(interp, objv[2], &hmPtr) != TCL_OK) {
1895        return TCL_ERROR;
1896    }
1897    const char *tag;
1898    tag = Tcl_GetString(objv[2]);
1899    TransferFunction *tfPtr;
1900    tfPtr = hmPtr->transferFunction();
1901    if (tfPtr == NULL) {
1902        Tcl_AppendResult(interp, "no transfer function defined for heightmap"
1903                         " \"", tag, "\"", (char*)NULL);
1904        return TCL_ERROR;
1905    }
1906    int w, h;
1907    if ((Tcl_GetIntFromObj(interp, objv[3], &w) != TCL_OK) ||
1908        (Tcl_GetIntFromObj(interp, objv[4], &h) != TCL_OK)) {
1909        return TCL_ERROR;
1910    }
1911    if (HeightMap::updatePending) {
1912        NanoVis::setHeightmapRanges();
1913    }
1914    NanoVis::renderLegend(tfPtr, HeightMap::valueMin, HeightMap::valueMax, 
1915                          w, h, tag);
1916    return TCL_OK;
1917}
1918
1919static int
1920HeightMapPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
1921                   Tcl_Obj *const *objv)
1922{
1923    RenderContext::PolygonMode mode;
1924    if (GetPolygonMode(interp, objv[2], &mode) != TCL_OK) {
1925        return TCL_ERROR;
1926    }
1927    NanoVis::renderContext->setPolygonMode(mode);
1928    NanoVis::eventuallyRedraw();
1929    return TCL_OK;
1930}
1931
1932static int
1933HeightMapShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1934                 Tcl_Obj *const *objv)
1935{
1936    RenderContext::ShadingModel model;
1937    if (GetShadingModel(interp, objv[2], &model) != TCL_OK) {
1938        return TCL_ERROR;
1939    }
1940    NanoVis::renderContext->setShadingModel(model);
1941    NanoVis::eventuallyRedraw();
1942    return TCL_OK;
1943}
1944
1945static int
1946HeightMapTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1947                     Tcl_Obj *const *objv)
1948{
1949    const char *name;
1950    name = Tcl_GetString(objv[2]);
1951    TransferFunction *tfPtr;
1952    tfPtr = NanoVis::getTransfunc(name);
1953    if (tfPtr == NULL) {
1954        Tcl_AppendResult(interp, "transfer function \"", name,
1955                         "\" is not defined", (char*)NULL);
1956        return TCL_ERROR;
1957    }
1958    std::vector<HeightMap *> imap;
1959    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
1960        return TCL_ERROR;
1961    }
1962    std::vector<HeightMap *>::iterator iter;
1963    for (iter = imap.begin(); iter != imap.end(); iter++) {
1964        (*iter)->transferFunction(tfPtr);
1965    }
1966    NanoVis::eventuallyRedraw();
1967    return TCL_OK;
1968}
1969
1970
1971static int
1972HeightMapOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1973                   Tcl_Obj *const *objv)
1974{
1975    float opacity;
1976    if (GetFloatFromObj(interp, objv[2], &opacity) != TCL_OK) {
1977        return TCL_ERROR;
1978    }
1979    std::vector<HeightMap *> heightmaps;
1980    if (GetHeightMaps(interp, objc - 3, objv + 3, &heightmaps) != TCL_OK) {
1981        return TCL_ERROR;
1982    }
1983    std::vector<HeightMap *>::iterator iter;
1984    for (iter = heightmaps.begin(); iter != heightmaps.end(); iter++) {
1985        (*iter)->opacity(opacity);
1986    }
1987    NanoVis::eventuallyRedraw();
1988    return TCL_OK;
1989}
1990
1991static Rappture::CmdSpec heightMapOps[] = {
1992    {"create",       2, HeightMapCreateOp,      10, 10, "tag xmin ymin xmax ymax xnum ynum values",},
1993    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
1994    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
1995    {"legend",       2, HeightMapLegendOp,      5, 5, "index width height",},
1996    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
1997    {"opacity",      1, HeightMapOpacityOp,     3, 0, "value ?heightmap...? ",},
1998    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
1999    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
2000    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?heightmap...?",},
2001};
2002static int nHeightMapOps = NumCmdSpecs(heightMapOps);
2003
2004static int
2005HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2006             Tcl_Obj *const *objv)
2007{
2008    Tcl_ObjCmdProc *proc;
2009
2010    proc = Rappture::GetOpFromObj(interp, nHeightMapOps, heightMapOps,
2011                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2012    if (proc == NULL) {
2013        return TCL_ERROR;
2014    }
2015    return (*proc) (clientData, interp, objc, objv);
2016}
2017
2018static int
2019GridAxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2020                Tcl_Obj *const *objv)
2021{
2022    float r, g, b, a;
2023    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2024        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2025        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2026        return TCL_ERROR;
2027    }
2028    a = 1.0f;
2029    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2030        return TCL_ERROR;
2031    }
2032    if (NanoVis::grid) {
2033        NanoVis::grid->setAxisColor(r, g, b, a);
2034    }
2035    return TCL_OK;
2036}
2037
2038static int
2039GridAxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2040               Tcl_Obj *const *objv)
2041{
2042    int axis;
2043    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
2044        return TCL_ERROR;
2045    }
2046    if (NanoVis::grid != NULL) {
2047        Axis *axisPtr;
2048
2049        axisPtr = NULL;     /* Suppress compiler warning. */
2050        switch (axis) {
2051        case 0: axisPtr = &NanoVis::grid->xAxis; break;
2052        case 1: axisPtr = &NanoVis::grid->yAxis; break;
2053        case 2: axisPtr = &NanoVis::grid->zAxis; break;
2054        }
2055        axisPtr->name(Tcl_GetString(objv[3]));
2056        axisPtr->units(Tcl_GetString(objv[4]));
2057    }
2058    return TCL_OK;
2059}
2060
2061static int
2062GridLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2063                Tcl_Obj *const *objv)
2064{
2065    float r, g, b, a;
2066    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2067        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2068        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2069        return TCL_ERROR;
2070    }
2071    a = 1.0f;
2072    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2073        return TCL_ERROR;
2074    }
2075    if (NanoVis::grid) {
2076        NanoVis::grid->setLineColor(r, g, b, a);
2077    }
2078    return TCL_OK;
2079}
2080
2081static int
2082GridVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2083              Tcl_Obj *const *objv)
2084{
2085    bool visible;
2086    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2087        return TCL_ERROR;
2088    }
2089    NanoVis::grid->setVisible(visible);
2090    return TCL_OK;
2091}
2092
2093static Rappture::CmdSpec gridOps[] = {
2094    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2095    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2096    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2097    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2098};
2099static int nGridOps = NumCmdSpecs(gridOps);
2100
2101static int
2102GridCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2103        Tcl_Obj *const *objv)
2104{
2105    Tcl_ObjCmdProc *proc;
2106
2107    proc = Rappture::GetOpFromObj(interp, nGridOps, gridOps,
2108        Rappture::CMDSPEC_ARG1, objc, objv, 0);
2109    if (proc == NULL) {
2110        return TCL_ERROR;
2111    }
2112    return (*proc) (clientData, interp, objc, objv);
2113}
2114
2115static int
2116AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2117        Tcl_Obj *const *objv)
2118{
2119    if (objc < 2) {
2120        Tcl_AppendResult(interp, "wrong # args: should be \"",
2121                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2122        return TCL_ERROR;
2123    }
2124    const char *string = Tcl_GetString(objv[1]);
2125    char c = string[0];
2126    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2127        bool visible;
2128
2129        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2130            return TCL_ERROR;
2131        }
2132        NanoVis::axisOn = visible;
2133    } else {
2134        Tcl_AppendResult(interp, "bad axis option \"", string,
2135                         "\": should be visible", (char*)NULL);
2136        return TCL_ERROR;
2137    }
2138    return TCL_OK;
2139}
2140
2141#ifdef PLANE_CMD
2142static int 
2143PlaneAddOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2144           Tcl_Obj *const *objv)
2145{
2146    TRACE("load plane for 2D visualization command");
2147
2148    int index, w, h;
2149    if (objc != 4) {
2150        Tcl_AppendResult(interp, "wrong # args: should be \"", 
2151            Tcl_GetString(objv[0]), " plane_index w h \"", (char*)NULL);
2152        return TCL_ERROR;
2153    }
2154    if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) {
2155        return TCL_ERROR;
2156    }
2157    if (index >= NanoVis::numPlanes) {
2158        Tcl_AppendResult(interp, "Invalid plane_index", (char*)NULL);
2159        return TCL_ERROR;
2160    }
2161    if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) {
2162        return TCL_ERROR;
2163    }
2164    if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) {
2165        return TCL_ERROR;
2166    }
2167
2168    //Now read w*h*4 bytes. The server expects the plane to be a stream of
2169    //floats
2170    char *tmp = new char[int(w*h*sizeof(float))];
2171    if (tmp == NULL) {
2172        Tcl_AppendResult(interp, "can't allocate stream data", (char *)NULL);
2173        return TCL_ERROR;
2174    }
2175    bzero(tmp, w*h*4);
2176    int status = read(fileno(NanoVis::stdin), tmp, w*h*sizeof(float));
2177    if (status <= 0) {
2178        delete[] tmp;
2179        Tcl_AppendResult(interp, "Failed to read image data for plane", (char*)NULL);
2180        return TCL_ERROR;
2181    }
2182    NanoVis::plane[index] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, (float*)tmp);
2183    delete[] tmp;
2184    return TCL_OK;
2185}
2186
2187static int
2188PlaneLinkOp(ClientData clientData, Tcl_Interp *interp, int objc,
2189            Tcl_Obj *const *objv)
2190{
2191    TRACE("link the plane to the 2D renderer command");
2192
2193    int plane_index;
2194
2195    if (objc != 3) {
2196        Tcl_AppendResult(interp, "wrong # args: should be \"",
2197            Tcl_GetString(objv[0]), " plane_index transfunc_name \"", (char*)NULL);
2198        return TCL_ERROR;
2199    }
2200    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2201        return TCL_ERROR;
2202    }
2203    if (plane_index >= NanoVis::numPlanes) {
2204        Tcl_AppendResult(interp, "Invalid plane_index", (char*)NULL);
2205        return TCL_ERROR;
2206    }
2207    NanoVis::planeRenderer->addPlane(NanoVis::plane[plane_index],
2208                                     NanoVis::getTransfunc(Tcl_GetString(objv[2])));
2209    return TCL_OK;
2210}
2211
2212//Enable a 2D plane for render
2213//The plane_index is the index mantained in the 2D plane renderer
2214static int
2215PlaneEnableOp(ClientData clientData, Tcl_Interp *interp, int objc,
2216              Tcl_Obj *const *objv)
2217{
2218    TRACE("enable a plane so the 2D renderer can render it command");
2219
2220    if (objc != 3) {
2221        Tcl_AppendResult(interp, "wrong # args: should be \"", 
2222            Tcl_GetString(objv[0]), " plane_index mode \"", (char*)NULL);
2223        return TCL_ERROR;
2224    }
2225    int plane_index;
2226    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2227        return TCL_ERROR;
2228    }
2229    if (plane_index >= NanoVis::numPlanes) {
2230        Tcl_AppendResult(interp, "Invalid plane_index", (char*)NULL);
2231        return TCL_ERROR;
2232    } else if (plane_index < 0) {
2233        plane_index = -1;
2234    }
2235
2236    NanoVis::planeRenderer->setActivePlane(plane_index);
2237    return TCL_OK;
2238}
2239
2240static Rappture::CmdSpec planeOps[] = {
2241    {"active",     2, PlaneEnableOp,    3, 3, "planeIdx",},
2242    {"add",        2, PlaneAddOp,       5, 5, "planeIdx width height",},
2243    {"link",       1, PlaneLinkOp,      4, 4, "planeIdx transfunc_name",},
2244};
2245static int nPlaneOps = NumCmdSpecs(planeOps);
2246
2247static int
2248PlaneCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2249         Tcl_Obj *const *objv)
2250{
2251    Tcl_ObjCmdProc *proc;
2252
2253    proc = Rappture::GetOpFromObj(interp, nPlaneOps, planeOps,
2254                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2255    if (proc == NULL) {
2256        return TCL_ERROR;
2257    }
2258    return (*proc) (clientData, interp, objc, objv);
2259}
2260
2261#endif /*PLANE_CMD*/
2262
2263/*
2264 * This command should be Tcl procedure instead of a C command.  The reason
2265 * for this that 1) we are using a safe interpreter so we would need a master
2266 * interpreter to load the Tcl environment properly (including our "unirect2d"
2267 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2268 * easy to add new directories for procedures, since it's loaded into /tmp.
2269 *
2270 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2271 * to verify the structure and then pass it to the appropiate Tcl command
2272 * (heightmap, volume, etc). Our C command always creates a heightmap.
2273 */
2274static int
2275Unirect2dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2276             Tcl_Obj *const *objv)
2277{
2278    Rappture::Unirect2d *dataPtr = (Rappture::Unirect2d *)clientData;
2279
2280    return dataPtr->loadData(interp, objc, objv);
2281}
2282
2283/*
2284 * This command should be Tcl procedure instead of a C command.  The reason
2285 * for this that 1) we are using a safe interpreter so we would need a master
2286 * interpreter to load the Tcl environment properly (including our "unirect2d"
2287 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2288 * easy to add new directories for procedures, since it's loaded into /tmp.
2289 *
2290 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2291 * to verify the structure and then pass it to the appropiate Tcl command
2292 * (heightmap, volume, etc). Our C command always creates a heightmap.
2293 */
2294
2295static int
2296Unirect3dCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2297             Tcl_Obj *const *objv)
2298{
2299    Rappture::Unirect3d *dataPtr = (Rappture::Unirect3d *)clientData;
2300
2301    return dataPtr->loadData(interp, objc, objv);
2302}
2303
2304Tcl_Interp *
2305initTcl()
2306{
2307    /*
2308     * Ideally the connection is authenticated by nanoscale.  I still like the
2309     * idea of creating a non-safe master interpreter with a safe slave
2310     * interpreter.  Alias all the nanovis commands in the slave. That way we
2311     * can still run Tcl code within nanovis.  The eventual goal is to create
2312     * a test harness through the interpreter for nanovis.
2313     */
2314    Tcl_Interp *interp;
2315    interp = Tcl_CreateInterp();
2316    /*
2317    Tcl_MakeSafe(interp);
2318    */
2319    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
2320    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
2321    Tcl_CreateObjCommand(interp, "clientinfo",  ClientInfoCmd,  NULL, NULL);
2322    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
2323    if (FlowCmdInitProc(interp) != TCL_OK) {
2324        return NULL;
2325    }
2326    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
2327    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
2328    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
2329#ifdef PLANE_CMD
2330    Tcl_CreateObjCommand(interp, "plane",       PlaneCmd,       NULL, NULL);
2331#endif
2332    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
2333    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    NULL, NULL);
2334    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   NULL, NULL);
2335    Tcl_CreateObjCommand(interp, "unirect2d",   Unirect2dCmd,   NULL, NULL);
2336    Tcl_CreateObjCommand(interp, "unirect3d",   Unirect3dCmd,   NULL, NULL);
2337    Tcl_CreateObjCommand(interp, "up",          UpCmd,          NULL, NULL);
2338    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
2339
2340    Tcl_InitHashTable(&NanoVis::volumeTable, TCL_STRING_KEYS);
2341    Tcl_InitHashTable(&NanoVis::heightmapTable, TCL_STRING_KEYS);
2342    // create a default transfer function
2343    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2344        WARN("bad default transfer function:\n%s", 
2345             Tcl_GetStringResult(interp));
2346    }
2347    return interp;
2348}
2349
2350
Note: See TracBrowser for help on using the repository browser.