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

Last change on this file since 956 was 956, checked in by dkearney, 17 years ago

updated the process of querying data from dx files but using native dx library calls instead of calculating grid positions and data points myself.
the Rappture::DX::interpolate() function does not quite work as intended, but if you do not change the axis lengths you can get the original data values back.
created a new function !computeSimpleGradient, located in dxReaderCommon.cpp, as part of the effort to simplify !dxReader.cpp.
removed old code from !dxReader2.cpp

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