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

Last change on this file since 1984 was 1984, checked in by gah, 13 years ago

Clean up debugging/printing traces

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