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

Last change on this file since 3376 was 3376, checked in by gah, 10 years ago

new version of stats file handling without file locking

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