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

Last change on this file since 1089 was 1089, checked in by dkearney, 16 years ago

saving the world from tabs...one source file at a time

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