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

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