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

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

added missing assert.h headers

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