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

Last change on this file since 1163 was 1163, checked in by gah, 16 years ago

added stats counters

File size: 82.5 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) *        o Add bookkeeping for volumes, heightmaps, flows, etc. to track
36 *          1) id #  2) simulation # 3) include/exclude.  The include/exclude
37 *          is to indicate whether the item should contribute to the overall
38 *          limits of the axes.
39 */
40
41#include <tcl.h>
42#include "Trace.h"
43#include "Command.h"
44#include "nanovis.h"
45#include "CmdProc.h"
46
47#include <RpField1D.h>
48#include <RpFieldRect3D.h>
49#include <RpFieldPrism3D.h>
50#include <RpEncode.h>
51#include <RpOutcome.h>
52#include <RpBuffer.h>
53
54#include "transfer-function/TransferFunctionMain.h"
55#include "transfer-function/ControlPoint.h"
56#include "transfer-function/TransferFunctionGLUTWindow.h"
57#include "transfer-function/ColorGradientGLUTWindow.h"
58#include "transfer-function/ColorPaletteWindow.h"
59#include "transfer-function/MainWindow.h"
60
61#include "Nv.h"
62#include "PointSetRenderer.h"
63#include "PointSet.h"
64#include "ZincBlendeVolume.h"
65#include "NvLoadFile.h"
66#include "NvColorTableRenderer.h"
67#include "NvEventLog.h"
68#include "NvZincBlendeReconstructor.h"
69#include "VolumeInterpolator.h"
70#include "HeightMap.h"
71#include "Grid.h"
72#include "NvCamera.h"
73#include <RenderContext.h>
74#include <NvLIC.h>
75
76#define ISO_TEST                1
77#define PLANE_CMD               0
78#define __TEST_CODE__           0
79// FOR testing new functions
80#define _LOCAL_ZINC_TEST_       0
81
82#if _LOCAL_ZINC_TEST_
83#include "Test.h"
84#endif
85
86// EXTERN DECLARATIONS
87// in Nv.cpp
88
89// in nanovis.cpp
90extern vector<PointSet*> g_pointSet;
91extern int debug_flag;
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
106
107// default transfer function
108static const char def_transfunc[] =
109    "transfunc define default {\n\
110  0.0  1 1 1\n\
111  0.2  1 1 0\n\
112  0.4  0 1 0\n\
113  0.6  0 1 1\n\
114  0.8  0 0 1\n\
115  1.0  1 0 1\n\
116} {\n\
117  0.00  1.0\n\
118  0.05  0.0\n\
119  0.15  0.0\n\
120  0.20  1.0\n\
121  0.25  0.0\n\
122  0.35  0.0\n\
123  0.40  1.0\n\
124  0.45  0.0\n\
125  0.55  0.0\n\
126  0.60  1.0\n\
127  0.65  0.0\n\
128  0.75  0.0\n\
129  0.80  1.0\n\
130  0.85  0.0\n\
131  0.95  0.0\n\
132  1.00  1.0\n\
133}";
134
135static Tcl_ObjCmdProc AxisCmd;
136static Tcl_ObjCmdProc CameraCmd;
137static Tcl_ObjCmdProc CutplaneCmd;
138static Tcl_ObjCmdProc FlowCmd;
139static Tcl_ObjCmdProc GridCmd;
140static Tcl_ObjCmdProc LegendCmd;
141#if PLANE_CMD
142static Tcl_ObjCmdProc PlaneCmd;
143#endif
144static Tcl_ObjCmdProc ScreenCmd;
145static Tcl_ObjCmdProc SnapshotCmd;
146static Tcl_ObjCmdProc TransfuncCmd;
147static Tcl_ObjCmdProc UniRect2dCmd;
148static Tcl_ObjCmdProc UpCmd;
149static Tcl_ObjCmdProc VolumeCmd;
150
151static bool
152GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
153{
154    int value;
155
156    if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) {
157        return TCL_ERROR;
158    }
159    *boolPtr = (bool)value;
160    return TCL_OK;
161}
162
163static int
164GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
165{
166    double value;
167
168    if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
169        return TCL_ERROR;
170    }
171    *valuePtr = (float)value;
172    return TCL_OK;
173}
174
175static int
176GetCullMode(Tcl_Interp *interp, Tcl_Obj *objPtr,
177            graphics::RenderContext::CullMode *modePtr)
178{
179    const char *string = Tcl_GetString(objPtr);
180    if (strcmp(string, "none") == 0) {
181        *modePtr = graphics::RenderContext::NO_CULL;
182    } else if (strcmp(string, "front") == 0) {
183        *modePtr = graphics::RenderContext::FRONT;
184    } else if (strcmp(string, "back") == 0) {
185        *modePtr = graphics::RenderContext::BACK;
186    } else {
187        Tcl_AppendResult(interp, "invalid cull mode \"", string,
188                         "\": should be front, back, or none\"", (char *)NULL);
189        return TCL_ERROR;
190    }
191    return TCL_OK;
192}
193
194static int
195GetShadingModel(Tcl_Interp *interp, Tcl_Obj *objPtr,
196                graphics::RenderContext::ShadingModel *modelPtr)
197{
198    const char *string = Tcl_GetString(objPtr);
199
200    if (strcmp(string,"flat") == 0) {
201        *modelPtr = graphics::RenderContext::FLAT;
202    } else if (strcmp(string,"smooth") == 0) {
203        *modelPtr = graphics::RenderContext::SMOOTH;
204    } else {
205        Tcl_AppendResult(interp, "bad shading model \"", string,
206                         "\": should be flat or smooth", (char *)NULL);
207        return TCL_ERROR;
208    }
209    return TCL_OK;
210}
211
212static int
213GetPolygonMode(Tcl_Interp *interp, Tcl_Obj *objPtr,
214               graphics::RenderContext::PolygonMode *modePtr)
215{
216    const char *string = Tcl_GetString(objPtr);
217
218    if (strcmp(string,"wireframe") == 0) {
219        *modePtr = graphics::RenderContext::LINE;
220    } else if (strcmp(string,"fill") == 0) {
221        *modePtr = graphics::RenderContext::FILL;
222    } else {
223        Tcl_AppendResult(interp, "invalid polygon mode \"", string,
224                         "\": should be wireframe or fill\"", (char *)NULL);
225        return TCL_ERROR;
226    }
227    return TCL_OK;
228}
229
230/*
231 * -----------------------------------------------------------------------
232 *
233 * CreateHeightMap --
234 *
235 *      Creates a heightmap from the given the data. The format of the data
236 *      should be as follows:
237 *
238 *              xMin, xMax, xNum, yMin, yMax, yNum, heights...
239 *
240 *      xNum and yNum must be integer values, all others are real numbers.
241 *      The number of heights must be xNum * yNum;
242 *
243 * -----------------------------------------------------------------------
244 */
245static HeightMap *
246CreateHeightMap(ClientData clientData, Tcl_Interp *interp, int objc,
247                Tcl_Obj *const *objv)
248{
249    float xMin, yMin, xMax, yMax;
250    int xNum, yNum;
251
252    if (objc != 7) {
253        Tcl_AppendResult(interp,
254        "wrong # of values: should be xMin yMin xMax yMax xNum yNum heights",
255        (char *)NULL);
256        return NULL;
257    }
258    if ((GetFloatFromObj(interp, objv[0], &xMin) != TCL_OK) ||
259        (GetFloatFromObj(interp, objv[1], &yMin) != TCL_OK) ||
260        (GetFloatFromObj(interp, objv[2], &xMax) != TCL_OK) ||
261        (GetFloatFromObj(interp, objv[3], &yMax) != TCL_OK) ||
262        (Tcl_GetIntFromObj(interp, objv[4], &xNum) != TCL_OK) ||
263        (Tcl_GetIntFromObj(interp, objv[5], &yNum) != TCL_OK)) {
264        return NULL;
265    }
266    int nValues;
267    Tcl_Obj **elem;
268    if (Tcl_ListObjGetElements(interp, objv[6], &nValues, &elem) != TCL_OK) {
269        return NULL;
270    }
271    if ((xNum <= 0) || (yNum <= 0)) {
272        Tcl_AppendResult(interp, "bad number of x or y values", (char *)NULL);
273        return NULL;
274    }
275    if (nValues != (xNum * yNum)) {
276        Tcl_AppendResult(interp, "wrong # of heights", (char *)NULL);
277        return NULL;
278    }
279
280    float *heights;
281    heights = new float[nValues];
282    if (heights == NULL) {
283        Tcl_AppendResult(interp, "can't allocate array of heights",
284                         (char *)NULL);
285        return NULL;
286    }
287
288    int i;
289    for (i = 0; i < nValues; i++) {
290        if (GetFloatFromObj(interp, elem[i], heights + i) != TCL_OK) {
291            delete [] heights;
292            return NULL;
293        }
294    }
295    HeightMap* hmPtr;
296    hmPtr = new HeightMap();
297    hmPtr->setHeight(xMin, yMin, xMax, yMax, xNum, yNum, heights);
298    hmPtr->setColorMap(NanoVis::get_transfunc("default"));
299    hmPtr->setVisible(true);
300    hmPtr->setLineContourVisible(true);
301    delete [] heights;
302    return hmPtr;
303}
304
305/*
306 * ----------------------------------------------------------------------
307 *
308 * GetHeightMapIndex --
309 *
310 * ----------------------------------------------------------------------
311 */
312static int
313GetHeightMapIndex(Tcl_Interp *interp, Tcl_Obj *objPtr, unsigned int *indexPtr)
314{
315    int index;
316    if (Tcl_GetIntFromObj(interp, objPtr, &index) != TCL_OK) {
317        return TCL_ERROR;
318    }
319    if (index < 0) {
320        Tcl_AppendResult(interp, "can't have negative index \"",
321                         Tcl_GetString(objPtr), "\"", (char*)NULL);
322        return TCL_ERROR;
323    }
324    if (index >= (int)NanoVis::heightMap.size()) {
325        Tcl_AppendResult(interp, "index \"", Tcl_GetString(objPtr),
326                         "\" is out of range", (char*)NULL);
327        return TCL_ERROR;
328    }
329    *indexPtr = (unsigned int)index;
330    return TCL_OK;
331}
332
333/*
334 * ----------------------------------------------------------------------
335 *
336 * GetHeightMapFromObj --
337 *
338 * ----------------------------------------------------------------------
339 */
340static int
341GetHeightMapFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, HeightMap **hmPtrPtr)
342{
343    unsigned int index;
344    if (GetHeightMapIndex(interp, objPtr, &index) != TCL_OK) {
345        return TCL_ERROR;
346    }
347    HeightMap *hmPtr;
348    hmPtr = NanoVis::heightMap[index];
349    if (hmPtr == NULL) {
350        Tcl_AppendResult(interp, "no heightmap defined for index \"",
351                         Tcl_GetString(objPtr), "\"", (char*)NULL);
352        return TCL_ERROR;
353    }
354    *hmPtrPtr = hmPtr;
355    return TCL_OK;
356}
357
358/*
359 * ----------------------------------------------------------------------
360 * FUNCTION: GetVolumeIndex
361 *
362 * Used internally to decode a series of volume index values and
363 * store then in the specified vector.  If there are no volume index
364 * arguments, this means "all volumes" to most commands, so all
365 * active volume indices are stored in the vector.
366 *
367 * Updates pushes index values into the vector.  Returns TCL_OK or
368 * TCL_ERROR to indicate an error.
369 * ----------------------------------------------------------------------
370 */
371static int
372GetVolumeIndex(Tcl_Interp *interp, Tcl_Obj *objPtr, unsigned int *indexPtr)
373{
374    int index;
375    if (Tcl_GetIntFromObj(interp, objPtr, &index) != TCL_OK) {
376        return TCL_ERROR;
377    }
378    if (index < 0) {
379        Tcl_AppendResult(interp, "can't have negative index \"",
380                         Tcl_GetString(objPtr), "\"", (char*)NULL);
381        return TCL_ERROR;
382    }
383    if (index >= (int)NanoVis::volume.size()) {
384        Tcl_AppendResult(interp, "index \"", Tcl_GetString(objPtr),
385                         "\" is out of range", (char*)NULL);
386        return TCL_ERROR;
387    }
388    *indexPtr = (unsigned int)index;
389    return TCL_OK;
390}
391
392/*
393 * ----------------------------------------------------------------------
394 * FUNCTION: GetVolumeFromObj
395 *
396 * Used internally to decode a series of volume index values and
397 * store then in the specified vector.  If there are no volume index
398 * arguments, this means "all volumes" to most commands, so all
399 * active volume indices are stored in the vector.
400 *
401 * Updates pushes index values into the vector.  Returns TCL_OK or
402 * TCL_ERROR to indicate an error.
403 * ----------------------------------------------------------------------
404 */
405static int
406GetVolumeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Volume **volPtrPtr)
407{
408    unsigned int index;
409    if (GetVolumeIndex(interp, objPtr, &index) != TCL_OK) {
410        return TCL_ERROR;
411    }
412    Volume *vol;
413    vol = NanoVis::volume[index];
414    if (vol == NULL) {
415        Tcl_AppendResult(interp, "no volume defined for index \"",
416                         Tcl_GetString(objPtr), "\"", (char*)NULL);
417        return TCL_ERROR;
418    }
419    *volPtrPtr = vol;
420    return TCL_OK;
421}
422
423/*
424 * ----------------------------------------------------------------------
425 * FUNCTION: GetVolumeIndices()
426 *
427 * Used internally to decode a series of volume index values and
428 * store then in the specified vector.  If there are no volume index
429 * arguments, this means "all volumes" to most commands, so all
430 * active volume indices are stored in the vector.
431 *
432 * Updates pushes index values into the vector.  Returns TCL_OK or
433 * TCL_ERROR to indicate an error.
434 * ----------------------------------------------------------------------
435 */
436static int
437GetVolumeIndices(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
438                 vector<unsigned int>* vectorPtr)
439{
440    if (objc == 0) {
441        for (unsigned int n = 0; n < NanoVis::volume.size(); n++) {
442            if (NanoVis::volume[n] != NULL) {
443                vectorPtr->push_back(n);
444            }
445        }
446    } else {
447        for (int n = 0; n < objc; n++) {
448            unsigned int index;
449
450            if (GetVolumeIndex(interp, objv[n], &index) != TCL_OK) {
451                return TCL_ERROR;
452            }
453            if (NanoVis::volume[index] != NULL) {
454                vectorPtr->push_back(index);
455            }
456        }
457    }
458    return TCL_OK;
459}
460
461/*
462 * ----------------------------------------------------------------------
463 * FUNCTION: GetVolumes()
464 *
465 * Used internally to decode a series of volume index values and
466 * store then in the specified vector.  If there are no volume index
467 * arguments, this means "all volumes" to most commands, so all
468 * active volume indices are stored in the vector.
469 *
470 * Updates pushes index values into the vector.  Returns TCL_OK or
471 * TCL_ERROR to indicate an error.
472 * ----------------------------------------------------------------------
473 */
474static int
475GetVolumes(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
476           vector<Volume *>* vectorPtr)
477{
478    if (objc == 0) {
479        for (unsigned int n = 0; n < NanoVis::volume.size(); n++) {
480            if (NanoVis::volume[n] != NULL) {
481                vectorPtr->push_back(NanoVis::volume[n]);
482            }
483        }
484    } else {
485        for (int n = 0; n < objc; n++) {
486            Volume *volPtr;
487
488            if (GetVolumeFromObj(interp, objv[n], &volPtr) != TCL_OK) {
489                return TCL_ERROR;
490            }
491            vectorPtr->push_back(volPtr);
492        }
493    }
494    return TCL_OK;
495}
496
497/*
498 * ----------------------------------------------------------------------
499 * FUNCTION: GetHeightMaps()
500 *
501 * Used internally to decode a series of volume index values and
502 * store then in the specified vector.  If there are no volume index
503 * arguments, this means "all volumes" to most commands, so all
504 * active volume indices are stored in the vector.
505 *
506 * Updates pushes index values into the vector.  Returns TCL_OK or
507 * TCL_ERROR to indicate an error.
508 * ----------------------------------------------------------------------
509 */
510static int
511GetHeightMaps(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
512           vector<HeightMap *>* vectorPtr)
513{
514    if (objc == 0) {
515        for (unsigned int n = 0; n < NanoVis::heightMap.size(); n++) {
516            if (NanoVis::heightMap[n] != NULL) {
517                vectorPtr->push_back(NanoVis::heightMap[n]);
518            }
519        }
520    } else {
521        for (int n = 0; n < objc; n++) {
522            HeightMap *hmPtr;
523
524            if (GetHeightMapFromObj(interp, objv[n], &hmPtr) != TCL_OK) {
525                return TCL_ERROR;
526            }
527            vectorPtr->push_back(hmPtr);
528        }
529    }
530    return TCL_OK;
531}
532
533
534/*
535 * ----------------------------------------------------------------------
536 * FUNCTION: GetAxis()
537 *
538 * Used internally to decode an axis value from a string ("x", "y",
539 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
540 * along with a value in valPtr.  Otherwise, it returns TCL_ERROR
541 * and an error message in the interpreter.
542 * ----------------------------------------------------------------------
543 */
544static int
545GetAxis(Tcl_Interp *interp, const char *string, int *indexPtr)
546{
547    if (string[1] == '\0') {
548        char c;
549
550        c = tolower((unsigned char)string[0]);
551        if (c == 'x') {
552            *indexPtr = 0;
553            return TCL_OK;
554        } else if (c == 'y') {
555            *indexPtr = 1;
556            return TCL_OK;
557        } else if (c == 'z') {
558            *indexPtr = 2;
559            return TCL_OK;
560        }
561        /*FALLTHRU*/
562    }
563    Tcl_AppendResult(interp, "bad axis \"", string,
564                     "\": should be x, y, or z", (char*)NULL);
565    return TCL_ERROR;
566}
567
568/*
569 * ----------------------------------------------------------------------
570 * FUNCTION: GetAxisFromObj()
571 *
572 * Used internally to decode an axis value from a string ("x", "y",
573 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
574 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
575 * and an error message in the interpreter.
576 * ----------------------------------------------------------------------
577 */
578static int
579GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr)
580{
581    return GetAxis(interp, Tcl_GetString(objPtr), indexPtr);
582}
583
584/*
585 * ----------------------------------------------------------------------
586 * FUNCTION: GetAxisDirFromObj()
587 *
588 * Used internally to decode an axis value from a string ("x", "y",
589 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
590 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
591 * and an error message in the interpreter.
592 * ----------------------------------------------------------------------
593 */
594static int
595GetAxisDirFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr, int *dirPtr)
596{
597    const char *string = Tcl_GetString(objPtr);
598
599    int sign = 1;
600    if (*string == '-') {
601        sign = -1;
602        string++;
603    }
604    if (GetAxis(interp, string, indexPtr) != TCL_OK) {
605        return TCL_ERROR;
606    }
607    if (dirPtr != NULL) {
608        *dirPtr = sign;
609    }
610    return TCL_OK;
611}
612
613/*
614 * ----------------------------------------------------------------------
615 * FUNCTION: GetColor()
616 *
617 * Used internally to decode a color value from a string ("R G B")
618 * as a list of three numbers 0-1.  Returns TCL_OK if successful,
619 * along with RGB values in rgbPtr.  Otherwise, it returns TCL_ERROR
620 * and an error message in the interpreter.
621 * ----------------------------------------------------------------------
622 */
623static int
624GetColor(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, float *rgbPtr)
625{
626    if (objc < 3) {
627        Tcl_AppendResult(interp, "missing color values\": ",
628                         "should list of R G B values 0.0 - 1.0", (char*)NULL);
629        return TCL_ERROR;
630    }
631    if ((GetFloatFromObj(interp, objv[0], rgbPtr + 0) != TCL_OK) ||
632        (GetFloatFromObj(interp, objv[1], rgbPtr + 1) != TCL_OK) ||
633        (GetFloatFromObj(interp, objv[2], rgbPtr + 2) != TCL_OK)) {
634        return TCL_ERROR;
635    }
636    return TCL_OK;
637}
638
639
640/*
641 * -----------------------------------------------------------------------
642 *
643 * GetDataStream --
644 *
645 *      Read the requested number of bytes from standard input into the given
646 *      buffer.  The buffer is then decompressed and decoded.
647 *
648 * -----------------------------------------------------------------------
649 */
650static int
651GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes)
652{
653    char buffer[8096];
654
655    clearerr(NanoVis::stdin);
656    while (nBytes > 0) {
657        unsigned int chunk;
658        int nRead;
659
660        chunk = (sizeof(buffer) < (unsigned int) nBytes) ?
661            sizeof(buffer) : nBytes;
662        nRead = fread(buffer, sizeof(char), chunk, NanoVis::stdin);
663        if (ferror(NanoVis::stdin)) {
664            Tcl_AppendResult(interp, "while reading data stream: ",
665                             Tcl_PosixError(interp), (char*)NULL);
666            return TCL_ERROR;
667        }
668        if (feof(NanoVis::stdin)) {
669            Tcl_AppendResult(interp, "premature EOF while reading data stream",
670                             (char*)NULL);
671            return TCL_ERROR;
672        }
673        buf.append(buffer, nRead);
674        nBytes -= nRead;
675    }
676    {
677        Rappture::Outcome err;
678
679        err = Rappture::encoding::decode(buf, RPENC_Z|RPENC_B64|RPENC_HDR);
680        if (err) {
681            printf("ERROR -- DECODING\n");
682            fflush(stdout);
683            Tcl_AppendResult(interp, err.remark().c_str(), (char*)NULL);
684            return TCL_ERROR;
685        }
686    }
687    return TCL_OK;
688}
689
690static int
691CameraAimOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
692{
693    double x0, y0, z0;
694    if ((Tcl_GetDoubleFromObj(interp, objv[2], &x0) != TCL_OK) ||
695        (Tcl_GetDoubleFromObj(interp, objv[3], &y0) != TCL_OK) ||
696        (Tcl_GetDoubleFromObj(interp, objv[4], &z0) != TCL_OK)) {
697        return TCL_ERROR;
698    }
699    NanoVis::cam->aim(x0, y0, z0);
700    return TCL_OK;
701}
702
703static int
704CameraAngleOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
705{
706    double xangle, yangle, zangle;
707    if ((Tcl_GetDoubleFromObj(interp, objv[2], &xangle) != TCL_OK) ||
708        (Tcl_GetDoubleFromObj(interp, objv[3], &yangle) != TCL_OK) ||
709        (Tcl_GetDoubleFromObj(interp, objv[4], &zangle) != TCL_OK)) {
710        return TCL_ERROR;
711    }
712    NanoVis::cam->rotate(xangle, yangle, zangle);
713    return TCL_OK;
714}
715
716static int
717CameraZoomOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
718{
719    double zoom;
720    if (Tcl_GetDoubleFromObj(interp, objv[2], &zoom) != TCL_OK) {
721        return TCL_ERROR;
722    }
723    NanoVis::zoom(zoom);
724    return TCL_OK;
725}
726
727static Rappture::CmdSpec cameraOps[] = {
728    {"aim",     2, CameraAimOp,      5, 5, "x y z",},
729    {"angle",   2, CameraAngleOp,    5, 5, "xAngle yAngle zAngle",},
730    {"zoom",    1, CameraZoomOp,     3, 3, "factor",},
731};
732static int nCameraOps = NumCmdSpecs(cameraOps);
733
734/*
735 * ----------------------------------------------------------------------
736 * CLIENT COMMAND:
737 *   camera aim <x0> <y0> <z0>
738 *   camera angle <xAngle> <yAngle> <zAngle>
739 *   camera zoom <factor>
740 *
741 * Clients send these commands to manipulate the camera.  The "angle"
742 * option controls the angle of the camera around the focal point.
743 * The "zoom" option sets the zoom factor, moving the camera in
744 * and out.
745 * ----------------------------------------------------------------------
746 */
747static int
748CameraCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
749{
750    Tcl_ObjCmdProc *proc;
751
752    proc = Rappture::GetOpFromObj(interp, nCameraOps, cameraOps,
753                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
754    if (proc == NULL) {
755        return TCL_ERROR;
756    }
757    return (*proc) (cdata, interp, objc, objv);
758}
759
760/*ARGSUSED*/
761static int
762SnapshotCmd(ClientData cdata, Tcl_Interp *interp, int objc,
763              Tcl_Obj *const *objv)
764{
765    int w, h;
766
767    w = NanoVis::win_width, h = NanoVis::win_height;
768    NanoVis::resize_offscreen_buffer(2048, 2048);
769#ifdef notdef
770    NanoVis::cam->set_screen_size(0, 0, NanoVis::win_width,NanoVis::win_height);
771    NanoVis::cam->set_screen_size(30, 90, 2048 - 60, 2048 - 120);
772#endif
773    NanoVis::offscreen_buffer_capture();  //enable offscreen render
774    NanoVis::display();
775    NanoVis::read_screen();
776#ifdef notdef
777    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
778#endif
779    NanoVis::ppm_write("nv>print -bytes");
780    NanoVis::resize_offscreen_buffer(w, h);
781    return TCL_OK;
782}
783
784static int
785CutplanePositionOp(ClientData cdata, Tcl_Interp *interp, int objc,
786                   Tcl_Obj *const *objv)
787{
788    float relval;
789    if (GetFloatFromObj(interp, objv[2], &relval) != TCL_OK) {
790        return TCL_ERROR;
791    }
792
793    // keep this just inside the volume so it doesn't disappear
794    if (relval < 0.01f) {
795        relval = 0.01f;
796    } else if (relval > 0.99f) {
797        relval = 0.99f;
798    }
799
800    int axis;
801    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
802        return TCL_ERROR;
803    }
804
805    vector<Volume *> ivol;
806    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
807        return TCL_ERROR;
808    }
809    vector<Volume *>::iterator iter;
810    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
811        (*iter)->move_cutplane(axis, relval);
812    }
813    return TCL_OK;
814}
815
816static int
817CutplaneStateOp(ClientData cdata, Tcl_Interp *interp, int objc,
818                Tcl_Obj *const *objv)
819{
820    bool state;
821    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
822        return TCL_ERROR;
823    }
824
825    int axis;
826    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
827        return TCL_ERROR;
828    }
829
830    vector<Volume *> ivol;
831    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
832        return TCL_ERROR;
833    }
834    if (state) {
835        vector<Volume *>::iterator iter;
836        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
837            (*iter)->enable_cutplane(axis);
838        }
839    } else {
840        vector<Volume *>::iterator iter;
841        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
842            (*iter)->disable_cutplane(axis);
843        }
844    }
845    return TCL_OK;
846}
847
848static Rappture::CmdSpec cutplaneOps[] = {
849    {"position", 1, CutplanePositionOp, 4, 0, "bool axis ?indices?",},
850    {"state",    1, CutplaneStateOp,    4, 0, "relval axis ?indices?",},
851};
852static int nCutplaneOps = NumCmdSpecs(cutplaneOps);
853
854/*
855 * ----------------------------------------------------------------------
856 * CLIENT COMMAND:
857 *   cutplane state on|off <axis> ?<volume>...?
858 *   cutplane position <relvalue> <axis> ?<volume>...?
859 *
860 * Clients send these commands to manipulate the cutplanes in one or
861 * more data volumes.  The "state" command turns a cutplane on or
862 * off.  The "position" command changes the position to a relative
863 * value in the range 0-1.  The <axis> can be x, y, or z.  These
864 * options are applied to the volumes represented by one or more
865 * <volume> indices.  If no volumes are specified, then all volumes
866 * are updated.
867 * ----------------------------------------------------------------------
868 */
869static int
870CutplaneCmd(ClientData cdata, Tcl_Interp *interp, int objc,
871            Tcl_Obj *const *objv)
872{
873    Tcl_ObjCmdProc *proc;
874
875    proc = Rappture::GetOpFromObj(interp, nCutplaneOps, cutplaneOps,
876                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
877    if (proc == NULL) {
878        return TCL_ERROR;
879    }
880    return (*proc) (cdata, interp, objc, objv);
881}
882
883/*
884 * ----------------------------------------------------------------------
885 * CLIENT COMMAND:
886 *   legend <volumeIndex> <width> <height>
887 *
888 * Clients use this to generate a legend image for the specified
889 * transfer function.  The legend image is a color gradient from 0
890 * to one, drawn in the given transfer function.  The resulting image
891 * is returned in the size <width> x <height>.
892 * ----------------------------------------------------------------------
893 */
894static int
895LegendCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
896{
897    if (objc != 4) {
898        Tcl_AppendResult(interp, "wrong # args: should be \"",
899            Tcl_GetString(objv[0]), " volIndex width height\"", (char*)NULL);
900        return TCL_ERROR;
901    }
902
903    Volume *volPtr;
904    if (GetVolumeFromObj(interp, objv[1], &volPtr) != TCL_OK) {
905        return TCL_ERROR;
906    }
907    TransferFunction *tf;
908    tf = NanoVis::vol_renderer->get_volume_shading(volPtr);
909    if (tf == NULL) {
910        Tcl_AppendResult(interp, "no transfer function defined for volume \"",
911                         Tcl_GetString(objv[1]), "\"", (char*)NULL);
912        return TCL_ERROR;
913    }
914    const char *label;
915    label = Tcl_GetString(objv[1]);
916
917    int w, h;
918    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
919        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
920        return TCL_ERROR;
921    }
922    if (volPtr->update_pending) {
923        NanoVis::SetVolumeRanges();
924    }
925    NanoVis::render_legend(tf,
926                           NanoVis::grid->yAxis.min(),
927                           NanoVis::grid->yAxis.max(), 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
1914static int
1915HeightMapDataFollowsOp(ClientData cdata, Tcl_Interp *interp, int objc,
1916                       Tcl_Obj *const *objv)
1917{
1918    Rappture::Buffer buf;
1919    int nBytes;
1920
1921    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
1922        return TCL_ERROR;
1923    }
1924    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
1925        return TCL_ERROR;
1926    }
1927    buf.append("\0", 1);
1928    int result;
1929    result = Tcl_Eval(interp, buf.bytes());
1930    if (result != TCL_OK) {
1931        fprintf(NanoVis::logfile, "error in command: %s\n",
1932                Tcl_GetStringResult(interp));
1933        fflush(NanoVis::logfile);
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
2116
2117static int
2118HeightMapTopView(ClientData data, Tcl_Interp *interp, int objc,
2119                Tcl_Obj *const *objv)
2120{
2121
2122    // the variables below should be reassigned
2123    int heightmap_index = 0;
2124    int image_width = 512;
2125    int image_height = 512;
2126    HeightMap* heightmap = 0;
2127
2128    // HELP ME
2129    // GEORGE
2130
2131
2132    NanoVis::render_2d_contour(heightmap, image_width, image_height);
2133   
2134    return TCL_OK;
2135}
2136
2137static int
2138HeightMapTestOp(ClientData cdata, Tcl_Interp *interp, int objc,
2139                Tcl_Obj *const *objv)
2140{
2141    srand((unsigned)time(NULL));
2142
2143    int size = 20 * 200;
2144    double sigma = 5.0;
2145    double mean = exp(0.0) / (sigma * sqrt(2.0));
2146    float* data = (float*) malloc(sizeof(float) * size);
2147
2148    float x;
2149    for (int i = 0; i < size; ++i) {
2150        x = - 10 + i%20;
2151        data[i] = exp(- (x * x)/(2 * sigma * sigma)) /
2152            (sigma * sqrt(2.0)) / mean * 2 + 1000;
2153    }
2154
2155    HeightMap* hmPtr = new HeightMap();
2156    float minx = 0.0f;
2157    float maxx = 1.0f;
2158    float miny = 0.5f;
2159    float maxy = 3.5f;
2160    hmPtr->setHeight(minx, miny, maxx, maxy, 20, 200, data);
2161    hmPtr->setColorMap(NanoVis::get_transfunc("default"));
2162    hmPtr->setVisible(true);
2163    hmPtr->setLineContourVisible(true);
2164    NanoVis::grid->setVisible(true);
2165    NanoVis::heightMap.push_back(hmPtr);
2166    return TCL_OK;
2167}
2168
2169static int
2170HeightMapTransFuncOp(ClientData cdata, Tcl_Interp *interp, int objc,
2171                     Tcl_Obj *const *objv)
2172{
2173    const char *name;
2174    name = Tcl_GetString(objv[2]);
2175    TransferFunction *tf;
2176    tf = NanoVis::get_transfunc(name);
2177    if (tf == NULL) {
2178        Tcl_AppendResult(interp, "transfer function \"", name,
2179                         "\" is not defined", (char*)NULL);
2180        return TCL_ERROR;
2181    }
2182    vector<HeightMap *> imap;
2183    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
2184        return TCL_ERROR;
2185    }
2186    vector<HeightMap *>::iterator iter;
2187    for (iter = imap.begin(); iter != imap.end(); iter++) {
2188        (*iter)->setColorMap(tf);
2189    }
2190    return TCL_OK;
2191}
2192
2193static Rappture::CmdSpec heightMapOps[] = {
2194    {"create",       2, HeightMapCreateOp,      9, 9,
2195     "xmin ymin xmax ymax xnum ynum values",},
2196    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
2197    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
2198    {"legend",       2, HeightMapLegendOp,      5, 5, "index width height",},
2199    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
2200    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
2201    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
2202    {"test",         2, HeightMapTestOp,        2, 2, "",},
2203    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?indices?",},
2204
2205    // HELP ME
2206    // GOERGE
2207    {"topview",      1, HeightMapTopView,     3, 3, "model",},
2208};
2209static int nHeightMapOps = NumCmdSpecs(heightMapOps);
2210
2211static int
2212HeightMapCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2213{
2214    Tcl_ObjCmdProc *proc;
2215
2216    proc = Rappture::GetOpFromObj(interp, nHeightMapOps, heightMapOps,
2217                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2218    if (proc == NULL) {
2219        return TCL_ERROR;
2220    }
2221    return (*proc) (cdata, interp, objc, objv);
2222}
2223
2224static int
2225GridAxisColorOp(ClientData cdata, Tcl_Interp *interp, int objc,
2226                Tcl_Obj *const *objv)
2227{
2228    float r, g, b, a;
2229    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2230        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2231        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2232        return TCL_ERROR;
2233    }
2234    a = 1.0f;
2235    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2236        return TCL_ERROR;
2237    }
2238    if (NanoVis::grid) {
2239        NanoVis::grid->setAxisColor(r, g, b, a);
2240    }
2241    return TCL_OK;
2242}
2243
2244static int
2245GridAxisNameOp(ClientData cdata, Tcl_Interp *interp, int objc,
2246               Tcl_Obj *const *objv)
2247{
2248    int axis;
2249    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
2250        return TCL_ERROR;
2251    }
2252    if (NanoVis::grid != NULL) {
2253        Axis *axisPtr;
2254
2255        axisPtr = NULL;     /* Suppress compiler warning. */
2256        switch (axis) {
2257        case 0: axisPtr = &NanoVis::grid->xAxis; break;
2258        case 1: axisPtr = &NanoVis::grid->yAxis; break;
2259        case 2: axisPtr = &NanoVis::grid->zAxis; break;
2260        }
2261        axisPtr->name(Tcl_GetString(objv[3]));
2262        axisPtr->units(Tcl_GetString(objv[4]));
2263    }
2264    return TCL_OK;
2265}
2266
2267static int
2268GridLineColorOp(ClientData cdata, Tcl_Interp *interp, int objc,
2269                Tcl_Obj *const *objv)
2270{
2271    float r, g, b, a;
2272    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2273        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2274        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2275        return TCL_ERROR;
2276    }
2277    a = 1.0f;
2278    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2279        return TCL_ERROR;
2280    }
2281    if (NanoVis::grid) {
2282        NanoVis::grid->setLineColor(r, g, b, a);
2283    }
2284    return TCL_OK;
2285}
2286
2287static int
2288GridVisibleOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2289{
2290    bool visible;
2291    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2292        return TCL_ERROR;
2293    }
2294    NanoVis::grid->setVisible(visible);
2295    return TCL_OK;
2296}
2297
2298static Rappture::CmdSpec gridOps[] = {
2299    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2300    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2301    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2302    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2303};
2304static int nGridOps = NumCmdSpecs(gridOps);
2305
2306static int
2307GridCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2308{
2309    Tcl_ObjCmdProc *proc;
2310
2311    proc = Rappture::GetOpFromObj(interp, nGridOps, gridOps,
2312        Rappture::CMDSPEC_ARG1, objc, objv, 0);
2313    if (proc == NULL) {
2314        return TCL_ERROR;
2315    }
2316    return (*proc) (cdata, interp, objc, objv);
2317}
2318
2319static int
2320AxisCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2321{
2322    if (objc < 2) {
2323        Tcl_AppendResult(interp, "wrong # args: should be \"",
2324                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2325        return TCL_ERROR;
2326    }
2327    const char *string = Tcl_GetString(objv[1]);
2328    char c = string[0];
2329    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2330        bool visible;
2331
2332        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2333            return TCL_ERROR;
2334        }
2335        NanoVis::axis_on = visible;
2336    } else {
2337        Tcl_AppendResult(interp, "bad axis option \"", string,
2338                         "\": should be visible", (char*)NULL);
2339        return TCL_ERROR;
2340    }
2341    return TCL_OK;
2342}
2343
2344#if PLANE_CMD
2345static int
2346PlaneNewOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2347{
2348    fprintf(stderr, "load plane for 2D visualization command\n");
2349    int index, w, h;
2350    if (objc != 4) {
2351        Tcl_AppendResult(interp, "wrong # args: should be \"",
2352            Tcl_GetString(objv[0]), " plane_index w h \"", (char*)NULL);
2353        return TCL_ERROR;
2354    }
2355    if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) {
2356        return TCL_ERROR;
2357    }
2358    if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) {
2359        return TCL_ERROR;
2360    }
2361    if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) {
2362        return TCL_ERROR;
2363    }
2364
2365    //Now read w*h*4 bytes. The server expects the plane to be a stream of
2366    //floats
2367    char* tmp = new char[int(w*h*sizeof(float))];
2368    if (tmp == NULL) {
2369        Tcl_AppendResult(interp, "can't allocate stream data", (char *)NULL);
2370        return TCL_ERROR;
2371    }
2372    bzero(tmp, w*h*4);
2373    int status = read(0, tmp, w*h*sizeof(float));
2374    if (status <= 0) {
2375        exit(0);                // Bail out on read error?  Should log the
2376                                // error and return a non-zero exit status.
2377    }
2378    plane[index] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, (float*)tmp);
2379    delete[] tmp;
2380    return TCL_OK;
2381}
2382
2383
2384static int
2385PlaneLinkOp(ClientData cdata, Tcl_Interp *interp, int objc,
2386            Tcl_Obj *const *objv)
2387{
2388    fprintf(stderr, "link the plane to the 2D renderer command\n");
2389
2390    int plane_index, tf_index;
2391
2392    if (objc != 3) {
2393        Tcl_AppendResult(interp, "wrong # args: should be \"",
2394            Tcl_GetString(objv[0]), " plane_index tf_index \"", (char*)NULL);
2395        return TCL_ERROR;
2396    }
2397    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2398        return TCL_ERROR;
2399    }
2400    if (Tcl_GetIntFromObj(interp, objv[2], &tf_index) != TCL_OK) {
2401        return TCL_ERROR;
2402    }
2403    //plane_render->add_plane(plane[plane_index], tf[tf_index]);
2404    return TCL_OK;
2405}
2406
2407//Enable a 2D plane for render
2408//The plane_index is the index mantained in the 2D plane renderer
2409static int
2410PlaneEnableOp(ClientData cdata, Tcl_Interp *interp, int objc,
2411              Tcl_Obj *const *objv)
2412{
2413    fprintf(stderr,"enable a plane so the 2D renderer can render it command\n");
2414
2415    if (objc != 3) {
2416        Tcl_AppendResult(interp, "wrong # args: should be \"",
2417            Tcl_GetString(objv[0]), " plane_index mode \"", (char*)NULL);
2418        return TCL_ERROR;
2419    }
2420    int plane_index;
2421    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2422        return TCL_ERROR;
2423    }
2424    int mode;
2425    if (Tcl_GetIntFromObj(interp, objv[2], &mode) != TCL_OK) {
2426        return TCL_ERROR;
2427    }
2428    if (mode == 0) {
2429        plane_index = -1;
2430    }
2431    plane_render->set_active_plane(plane_index);
2432    return TCL_OK;
2433}
2434
2435static Rappture::CmdSpec planeOps[] = {
2436    {"enable",     1, PlaneEnableOp,    4, 4, "planeIdx mode",},
2437    {"link",       1, PlaneLinkOp,      4, 4, "planeIdx transfuncIdx",},
2438    {"new",        1, PlaneNewOp,       5, 5, "planeIdx width height",},
2439};
2440static int nPlaneOps = NumCmdSpecs(planeOps);
2441
2442static int
2443PlaneCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2444{
2445    Tcl_ObjCmdProc *proc;
2446
2447    proc = Rappture::GetOpFromObj(interp, nPlaneOps, planeOps,
2448                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2449    if (proc == NULL) {
2450        return TCL_ERROR;
2451    }
2452    return (*proc) (cdata, interp, objc, objv);
2453}
2454
2455#endif  /*PLANE_CMD*/
2456
2457/*
2458 * This command should be Tcl procedure instead of a C command.  The reason
2459 * for this that 1) we are using a safe interpreter so we would need a master
2460 * interpreter to load the Tcl environment properly (including our "unirect2d"
2461 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2462 * easy to add new directories for procedures, since it's loaded into /tmp.
2463 *
2464 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2465 * to verify the structure and then pass it to the appropiate Tcl command
2466 * (heightmap, volume, etc). Our C command always creates a heightmap.
2467 */
2468static int
2469UniRect2dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2470             Tcl_Obj *const *objv)
2471{
2472    int xNum, yNum, zNum;
2473    float xMin, yMin, xMax, yMax;
2474    float *zValues;
2475    const char *xUnits, *yUnits, *zUnits;
2476
2477    if ((objc & 0x01) == 0) {
2478        Tcl_AppendResult(interp, Tcl_GetString(objv[0]), ": ",
2479                "wrong number of arguments: should be key-value pairs",
2480                (char *)NULL);
2481        return TCL_ERROR;
2482    }
2483    zValues = NULL;
2484    xNum = yNum = zNum = 0;
2485    xMin = yMin = xMax = yMax = 0.0f;
2486    xUnits = yUnits = zUnits = NULL;
2487    int i;
2488    for (i = 1; i < objc; i += 2) {
2489        const char *string;
2490        char c;
2491
2492        string = Tcl_GetString(objv[i]);
2493        c = string[0];
2494        if ((c == 'x') && (strcmp(string, "xmin") == 0)) {
2495            if (GetFloatFromObj(interp, objv[i+1], &xMin) != TCL_OK) {
2496                return TCL_ERROR;
2497            }
2498        } else if ((c == 'x') && (strcmp(string, "xmax") == 0)) {
2499            if (GetFloatFromObj(interp, objv[i+1], &xMax) != TCL_OK) {
2500                return TCL_ERROR;
2501            }
2502        } else if ((c == 'x') && (strcmp(string, "xnum") == 0)) {
2503            if (Tcl_GetIntFromObj(interp, objv[i+1], &xNum) != TCL_OK) {
2504                return TCL_ERROR;
2505            }
2506            if (xNum <= 0) {
2507                Tcl_AppendResult(interp, "bad xnum value: must be > 0",
2508                                 (char *)NULL);
2509                return TCL_ERROR;
2510            }
2511        } else if ((c == 'x') && (strcmp(string, "xunits") == 0)) {
2512            xUnits = Tcl_GetString(objv[i+1]);
2513        } else if ((c == 'y') && (strcmp(string, "ymin") == 0)) {
2514            if (GetFloatFromObj(interp, objv[i+1], &yMin) != TCL_OK) {
2515                return TCL_ERROR;
2516            }
2517        } else if ((c == 'y') && (strcmp(string, "ymax") == 0)) {
2518            if (GetFloatFromObj(interp, objv[i+1], &yMax) != TCL_OK) {
2519                return TCL_ERROR;
2520            }
2521        } else if ((c == 'y') && (strcmp(string, "ynum") == 0)) {
2522            if (Tcl_GetIntFromObj(interp, objv[i+1], &yNum) != TCL_OK) {
2523                return TCL_ERROR;
2524            }
2525            if (yNum <= 0) {
2526                Tcl_AppendResult(interp, "bad ynum value: must be > 0",
2527                                 (char *)NULL);
2528                return TCL_ERROR;
2529            }
2530        } else if ((c == 'y') && (strcmp(string, "yunits") == 0)) {
2531            yUnits = Tcl_GetString(objv[i+1]);
2532        } else if ((c == 'z') && (strcmp(string, "zvalues") == 0)) {
2533            Tcl_Obj **zObj;
2534
2535            if (Tcl_ListObjGetElements(interp, objv[i+1], &zNum, &zObj)!= TCL_OK) {
2536                return TCL_ERROR;
2537            }
2538            int j;
2539            zValues = new float[zNum];
2540            for (j = 0; j < zNum; j++) {
2541                if (GetFloatFromObj(interp, zObj[j], zValues + j) != TCL_OK) {
2542                    return TCL_ERROR;
2543                }
2544            }
2545        } else if ((c == 'z') && (strcmp(string, "zunits") == 0)) {
2546            zUnits = Tcl_GetString(objv[i+1]);
2547        } else {
2548            Tcl_AppendResult(interp, "unknown key \"", string,
2549                "\": should be xmin, xmax, xnum, xunits, ymin, ymax, ynum, yunits, zvalues, or zunits",
2550                (char *)NULL);
2551            return TCL_ERROR;
2552        }
2553    }
2554    if (zValues == NULL) {
2555        Tcl_AppendResult(interp, "missing \"zvalues\" key", (char *)NULL);
2556        return TCL_ERROR;
2557    }
2558    if (zNum != (xNum * yNum)) {
2559        Tcl_AppendResult(interp, "wrong number of z values must be xnum*ynum",
2560                (char *)NULL);
2561        return TCL_ERROR;
2562    }
2563    HeightMap* hmPtr;
2564    hmPtr = new HeightMap();
2565
2566    // Must set units before the heights.
2567    hmPtr->xAxis.units(xUnits);
2568    hmPtr->yAxis.units(yUnits);
2569    hmPtr->zAxis.units(zUnits);
2570    hmPtr->wAxis.units(yUnits);
2571    hmPtr->setHeight(xMin, yMin, xMax, yMax, xNum, yNum, zValues);
2572    hmPtr->setColorMap(NanoVis::get_transfunc("default"));
2573    hmPtr->setVisible(true);
2574    hmPtr->setLineContourVisible(true);
2575    NanoVis::heightMap.push_back(hmPtr);
2576    return TCL_OK;
2577}
2578
2579
2580Tcl_Interp *
2581initTcl()
2582{
2583
2584    /*
2585     * Ideally the connection is authenticated by nanoscale.  I still like the
2586     * idea of creating a non-safe master interpreter with a safe slave
2587     * interpreter.  Alias all the nanovis commands in the slave. That way we
2588     * can still run Tcl code within nanovis.  The eventual goal is to create
2589     * a test harness through the interpreter for nanovis.
2590     */
2591    Tcl_Interp *interp;
2592    interp = Tcl_CreateInterp();
2593    Tcl_MakeSafe(interp);
2594
2595    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
2596    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
2597    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
2598    Tcl_CreateObjCommand(interp, "flow",        FlowCmd,        NULL, NULL);
2599    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
2600    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
2601    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
2602    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
2603    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    NULL, NULL);
2604    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   NULL, NULL);
2605    Tcl_CreateObjCommand(interp, "unirect2d",   UniRect2dCmd,   NULL, NULL);
2606    Tcl_CreateObjCommand(interp, "up",          UpCmd,          NULL, NULL);
2607    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
2608#if __TEST_CODE__
2609    Tcl_CreateObjCommand(interp, "test", TestCmd, NULL, NULL);
2610#endif
2611
2612    // create a default transfer function
2613    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2614        fprintf(NanoVis::logfile, "WARNING: bad default transfer function\n");
2615        fprintf(NanoVis::logfile, Tcl_GetStringResult(interp));
2616    }
2617    return interp;
2618}
2619
2620
Note: See TracBrowser for help on using the repository browser.