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

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

Remove XINETD define from nanovis. We only support server mode now, no glut
interaction loop with mouse/keyboard handlers. Fixes for trace logging to make
output closer to vtkvis: inlcude function name for trace messages, remove
newlines from format strings in macros since newlines get added by syslog.

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