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

Last change on this file since 3567 was 3567, checked in by ldelgass, 9 years ago

Refactor and cleanups in nanovis, mainly to switch to using STL hash tables
(TR1 required) instead of Tcl hash tables, split out Flow particles and boxes
to separate implementation files. The goal is to achieve better separation of
Tcl command parsing and the core graphics rendering objects and code.

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