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

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