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

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