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

Last change on this file since 1370 was 1370, checked in by vrinside, 15 years ago

improving the flow vis engine

  • particle advection for multiple vector field
  • specifying multiple advection planes
  • specifying the position of a particle injection plane
  • specifying the axis of a particle injection plane
  • specifying the visibility of particle injection planes
  • specifying the particle color for each particle injection plane
  • rendering device shapes

[NOTE] Currently, I commented out with the MACRO NEW_FLOW_ENGINE in config
To use this, please comment NEW_FLOW_ENGINE in

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