source: branches/nanovis2/packages/vizservers/nanovis/Command.cpp @ 3305

Last change on this file since 3305 was 3305, checked in by ldelgass, 11 years ago

sync with trunk

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