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

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

fixed the location of particles

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