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

Last change on this file since 915 was 915, checked in by gah, 15 years ago

more clean up

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