source: trunk/vizservers/nanovis/Command.cpp @ 930

Last change on this file since 930 was 930, checked in by gah, 17 years ago

Fixes for volume command parser

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