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

Last change on this file since 1028 was 1028, checked in by gah, 13 years ago

various cleanups

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