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

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

changes to allow panning and zooming (via scrollwhell)

File size: 83.5 KB
Line 
1
2/*
3 * ----------------------------------------------------------------------
4 * Command.cpp
5 *
6 *      This modules creates the Tcl interface to the nanovis server.  The
7 *      communication protocol of the server is the Tcl language.  Commands
8 *      given to the server by clients are executed in a safe interpreter and
9 *      the resulting image rendered offscreen is returned as BMP-formatted
10 *      image data.
11 *
12 * ======================================================================
13 *  AUTHOR:  Wei Qiao <qiaow@purdue.edu>
14 *           Michael McLennan <mmclennan@purdue.edu>
15 *           Purdue Rendering and Perceptualization Lab (PURPL)
16 *
17 *  Copyright (c) 2004-2006  Purdue Research Foundation
18 *
19 *  See the file "license.terms" for information on usage and
20 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 * ======================================================================
22 */
23
24/*
25 * TODO:  In no particular order...
26 *        x Convert to Tcl_CmdObj interface. (done)
27 *        o Use Tcl command option parser to reduce size of procedures, remove
28 *          lots of extra error checking code. (almost there)
29 *        o Convert GetVolumeIndices to GetVolumes.  Goal is to remove
30 *          all references of Nanovis::volume[] from this file.  Don't
31 *          want to know how volumes are stored. Same for heightmaps.
32 *        o Rationalize volume id scheme. Right now it's the index in
33 *          the vector. 1) Use a list instead of a vector. 2) carry
34 *          an id field that's a number that gets incremented each new volume.
35 *        x Create R2, matrix, etc. libraries. (done) *        o Add bookkeeping for volumes, heightmaps, flows, etc. to track
36 *          1) id #  2) simulation # 3) include/exclude.  The include/exclude
37 *          is to indicate whether the item should contribute to the overall
38 *          limits of the axes.
39 */
40
41#include <tcl.h>
42#include <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
696CameraPanOp(ClientData cdata, Tcl_Interp *interp, int objc,
697             Tcl_Obj *const *objv)
698{
699    double dx, dy, dz;
700    if ((Tcl_GetDoubleFromObj(interp, objv[2], &dx) != TCL_OK) ||
701        (Tcl_GetDoubleFromObj(interp, objv[3], &dy) != TCL_OK) ||
702        (Tcl_GetDoubleFromObj(interp, objv[4], &dz) != TCL_OK)) {
703        return TCL_ERROR;
704    }
705    NanoVis::cam->aim(dx, dy, dz);
706    NanoVis::cam->x(dx);
707    NanoVis::cam->y(dy);
708    NanoVis::cam->z(dz);
709    return TCL_OK;
710}
711
712static int
713CameraAimOp(ClientData cdata, Tcl_Interp *interp, int objc,
714            Tcl_Obj *const *objv)
715{
716    double x, y, z;
717    if ((Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK) ||
718        (Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) ||
719        (Tcl_GetDoubleFromObj(interp, objv[4], &z) != TCL_OK)) {
720        return TCL_ERROR;
721    }
722    NanoVis::cam->aim(x, y, z);
723    return TCL_OK;
724}
725
726static int
727CameraAngleOp(ClientData cdata, Tcl_Interp *interp, int objc,
728              Tcl_Obj *const *objv)
729{
730    double theta, phi, psi;
731    if ((Tcl_GetDoubleFromObj(interp, objv[2], &phi) != TCL_OK) ||
732        (Tcl_GetDoubleFromObj(interp, objv[3], &theta) != TCL_OK) ||
733        (Tcl_GetDoubleFromObj(interp, objv[4], &psi) != TCL_OK)) {
734        return TCL_ERROR;
735    }
736    NanoVis::cam->rotate(phi, theta, psi);
737    return TCL_OK;
738}
739
740
741static int
742CameraZoomOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
743{
744    double zoom;
745    if (Tcl_GetDoubleFromObj(interp, objv[2], &zoom) != TCL_OK) {
746        return TCL_ERROR;
747    }
748    NanoVis::cam->z(-2.5 / zoom);
749    return TCL_OK;
750}
751
752static Rappture::CmdSpec cameraOps[] = {
753    {"aim",     2, CameraAimOp,      5, 5, "x y z",},
754    {"angle",   2, CameraAngleOp,    5, 5, "xAngle yAngle zAngle",},
755    {"pan",     1, CameraPanOp,      5, 5, "x y z",},
756    {"zoom",    1, CameraZoomOp,     3, 3, "factor",},
757};
758static int nCameraOps = NumCmdSpecs(cameraOps);
759
760/*
761 * ----------------------------------------------------------------------
762 * CLIENT COMMAND:
763 *   camera aim <x0> <y0> <z0>
764 *   camera angle <xAngle> <yAngle> <zAngle>
765 *   camera zoom <factor>
766 *
767 * Clients send these commands to manipulate the camera.  The "angle"
768 * option controls the angle of the camera around the focal point.
769 * The "zoom" option sets the zoom factor, moving the camera in
770 * and out.
771 * ----------------------------------------------------------------------
772 */
773static int
774CameraCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
775{
776    Tcl_ObjCmdProc *proc;
777
778    proc = Rappture::GetOpFromObj(interp, nCameraOps, cameraOps,
779                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
780    if (proc == NULL) {
781        return TCL_ERROR;
782    }
783    return (*proc) (cdata, interp, objc, objv);
784}
785
786/*ARGSUSED*/
787static int
788SnapshotCmd(ClientData cdata, Tcl_Interp *interp, int objc,
789              Tcl_Obj *const *objv)
790{
791    int w, h;
792
793    w = NanoVis::win_width, h = NanoVis::win_height;
794    NanoVis::resize_offscreen_buffer(2048, 2048);
795#ifdef notdef
796    NanoVis::cam->set_screen_size(0, 0, NanoVis::win_width,NanoVis::win_height);
797    NanoVis::cam->set_screen_size(30, 90, 2048 - 60, 2048 - 120);
798#endif
799    NanoVis::offscreen_buffer_capture();  //enable offscreen render
800    NanoVis::display();
801    NanoVis::read_screen();
802#ifdef notdef
803    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
804#endif
805    NanoVis::ppm_write("nv>print -bytes");
806    NanoVis::resize_offscreen_buffer(w, h);
807    return TCL_OK;
808}
809
810static int
811CutplanePositionOp(ClientData cdata, Tcl_Interp *interp, int objc,
812                   Tcl_Obj *const *objv)
813{
814    float relval;
815    if (GetFloatFromObj(interp, objv[2], &relval) != TCL_OK) {
816        return TCL_ERROR;
817    }
818
819    // keep this just inside the volume so it doesn't disappear
820    if (relval < 0.01f) {
821        relval = 0.01f;
822    } else if (relval > 0.99f) {
823        relval = 0.99f;
824    }
825
826    int axis;
827    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
828        return TCL_ERROR;
829    }
830
831    vector<Volume *> ivol;
832    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
833        return TCL_ERROR;
834    }
835    vector<Volume *>::iterator iter;
836    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
837        (*iter)->move_cutplane(axis, relval);
838    }
839    return TCL_OK;
840}
841
842static int
843CutplaneStateOp(ClientData cdata, Tcl_Interp *interp, int objc,
844                Tcl_Obj *const *objv)
845{
846    bool state;
847    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
848        return TCL_ERROR;
849    }
850
851    int axis;
852    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
853        return TCL_ERROR;
854    }
855
856    vector<Volume *> ivol;
857    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
858        return TCL_ERROR;
859    }
860    if (state) {
861        vector<Volume *>::iterator iter;
862        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
863            (*iter)->enable_cutplane(axis);
864        }
865    } else {
866        vector<Volume *>::iterator iter;
867        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
868            (*iter)->disable_cutplane(axis);
869        }
870    }
871    return TCL_OK;
872}
873
874static Rappture::CmdSpec cutplaneOps[] = {
875    {"position", 1, CutplanePositionOp, 4, 0, "bool axis ?indices?",},
876    {"state",    1, CutplaneStateOp,    4, 0, "relval axis ?indices?",},
877};
878static int nCutplaneOps = NumCmdSpecs(cutplaneOps);
879
880/*
881 * ----------------------------------------------------------------------
882 * CLIENT COMMAND:
883 *   cutplane state on|off <axis> ?<volume>...?
884 *   cutplane position <relvalue> <axis> ?<volume>...?
885 *
886 * Clients send these commands to manipulate the cutplanes in one or
887 * more data volumes.  The "state" command turns a cutplane on or
888 * off.  The "position" command changes the position to a relative
889 * value in the range 0-1.  The <axis> can be x, y, or z.  These
890 * options are applied to the volumes represented by one or more
891 * <volume> indices.  If no volumes are specified, then all volumes
892 * are updated.
893 * ----------------------------------------------------------------------
894 */
895static int
896CutplaneCmd(ClientData cdata, Tcl_Interp *interp, int objc,
897            Tcl_Obj *const *objv)
898{
899    Tcl_ObjCmdProc *proc;
900
901    proc = Rappture::GetOpFromObj(interp, nCutplaneOps, cutplaneOps,
902                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
903    if (proc == NULL) {
904        return TCL_ERROR;
905    }
906    return (*proc) (cdata, interp, objc, objv);
907}
908
909/*
910 * ----------------------------------------------------------------------
911 * CLIENT COMMAND:
912 *   legend <volumeIndex> <width> <height>
913 *
914 * Clients use this to generate a legend image for the specified
915 * transfer function.  The legend image is a color gradient from 0
916 * to one, drawn in the given transfer function.  The resulting image
917 * is returned in the size <width> x <height>.
918 * ----------------------------------------------------------------------
919 */
920static int
921LegendCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
922{
923    if (objc != 4) {
924        Tcl_AppendResult(interp, "wrong # args: should be \"",
925            Tcl_GetString(objv[0]), " volIndex width height\"", (char*)NULL);
926        return TCL_ERROR;
927    }
928
929    Volume *volPtr;
930    if (GetVolumeFromObj(interp, objv[1], &volPtr) != TCL_OK) {
931        return TCL_ERROR;
932    }
933    TransferFunction *tf;
934    tf = NanoVis::vol_renderer->get_volume_shading(volPtr);
935    if (tf == NULL) {
936        Tcl_AppendResult(interp, "no transfer function defined for volume \"",
937                         Tcl_GetString(objv[1]), "\"", (char*)NULL);
938        return TCL_ERROR;
939    }
940    const char *label;
941    label = Tcl_GetString(objv[1]);
942
943    int w, h;
944    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
945        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
946        return TCL_ERROR;
947    }
948    if (volPtr->update_pending) {
949        NanoVis::SetVolumeRanges();
950    }
951    NanoVis::render_legend(tf,
952                           NanoVis::grid->yAxis.min(),
953                           NanoVis::grid->yAxis.max(), w, h, label);
954    return TCL_OK;
955}
956
957/*
958 * ----------------------------------------------------------------------
959 * CLIENT COMMAND:
960 *   screen <width> <height>
961 *
962 * Clients send this command to set the size of the rendering area.
963 * Future images are generated at the specified width/height.
964 * ----------------------------------------------------------------------
965 */
966static int
967ScreenCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
968{
969    if (objc != 3) {
970        Tcl_AppendResult(interp, "wrong # args: should be \"",
971                         Tcl_GetString(objv[0]), " width height\"", (char*)NULL);
972        return TCL_ERROR;
973    }
974
975    int w, h;
976    if ((Tcl_GetIntFromObj(interp, objv[1], &w) != TCL_OK) ||
977        (Tcl_GetIntFromObj(interp, objv[2], &h) != TCL_OK)) {
978        return TCL_ERROR;
979    }
980    NanoVis::resize_offscreen_buffer(w, h);
981    return TCL_OK;
982}
983
984/*
985 * ----------------------------------------------------------------------
986 * CLIENT COMMAND:
987 *   transfunc define <name> <colormap> <alphamap>
988 *     where <colormap> = { <v> <r> <g> <b> ... }
989 *           <alphamap> = { <v> <w> ... }
990 *
991 * Clients send these commands to manipulate the transfer functions.
992 * ----------------------------------------------------------------------
993 */
994static int
995TransfuncCmd(ClientData cdata, Tcl_Interp *interp, int objc,
996             Tcl_Obj *const *objv)
997{
998    if (objc < 2) {
999        Tcl_AppendResult(interp, "wrong # args: should be \"",
1000                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
1001        return TCL_ERROR;
1002    }
1003
1004    const char *string = Tcl_GetString(objv[1]);
1005    char c = string[0];
1006    if ((c == 'd') && (strcmp(string, "define") == 0)) {
1007        if (objc != 5) {
1008            Tcl_AppendResult(interp, "wrong # args: should be \"",
1009                Tcl_GetString(objv[0]), " define name colorMap alphaMap\"",
1010                (char*)NULL);
1011            return TCL_ERROR;
1012        }
1013
1014        // decode the data and store in a series of fields
1015        Rappture::Field1D rFunc, gFunc, bFunc, wFunc;
1016        int cmapc, wmapc, i;
1017        Tcl_Obj **cmapv;
1018        Tcl_Obj **wmapv;
1019
1020        wmapv = cmapv = NULL;
1021        if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
1022            return TCL_ERROR;
1023        }
1024        if ((cmapc % 4) != 0) {
1025            Tcl_AppendResult(interp, "wrong # elements is colormap: should be ",
1026                "{ v r g b ... }", (char*)NULL);
1027            return TCL_ERROR;
1028        }
1029        if (Tcl_ListObjGetElements(interp, objv[4], &wmapc, &wmapv) != TCL_OK) {
1030            return TCL_ERROR;
1031        }
1032        if ((wmapc % 2) != 0) {
1033            Tcl_AppendResult(interp, "wrong # elements in alphamap: should be ",
1034                " { v w ... }", (char*)NULL);
1035            return TCL_ERROR;
1036        }
1037        for (i = 0; i < cmapc; i += 4) {
1038            int j;
1039            double q[4];
1040
1041            for (j=0; j < 4; j++) {
1042                if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &q[j]) != TCL_OK) {
1043                    return TCL_ERROR;
1044                }
1045                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1046                    Tcl_AppendResult(interp, "bad colormap value \"",
1047                        Tcl_GetString(cmapv[i+j]),
1048                        "\": should be in the range 0-1", (char*)NULL);
1049                    return TCL_ERROR;
1050                }
1051            }
1052            rFunc.define(q[0], q[1]);
1053            gFunc.define(q[0], q[2]);
1054            bFunc.define(q[0], q[3]);
1055        }
1056        for (i=0; i < wmapc; i += 2) {
1057            double q[2];
1058            int j;
1059
1060            for (j=0; j < 2; j++) {
1061                if (Tcl_GetDoubleFromObj(interp, wmapv[i+j], &q[j]) != TCL_OK) {
1062                    return TCL_ERROR;
1063                }
1064                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1065                    Tcl_AppendResult(interp, "bad alphamap value \"",
1066                        Tcl_GetString(wmapv[i+j]),
1067                        "\": should be in the range 0-1", (char*)NULL);
1068                    return TCL_ERROR;
1069                }
1070            }
1071            wFunc.define(q[0], q[1]);
1072        }
1073        // sample the given function into discrete slots
1074        const int nslots = 256;
1075        float data[4*nslots];
1076        for (i=0; i < nslots; i++) {
1077            double x = double(i)/(nslots-1);
1078            data[4*i]   = rFunc.value(x);
1079            data[4*i+1] = gFunc.value(x);
1080            data[4*i+2] = bFunc.value(x);
1081            data[4*i+3] = wFunc.value(x);
1082        }
1083        // find or create this transfer function
1084        NanoVis::DefineTransferFunction(Tcl_GetString(objv[2]), nslots, data);
1085    } else {
1086        Tcl_AppendResult(interp, "bad option \"", string,
1087                "\": should be define", (char*)NULL);
1088        return TCL_ERROR;
1089    }
1090    return TCL_OK;
1091}
1092
1093/*
1094 * ----------------------------------------------------------------------
1095 * CLIENT COMMAND:
1096 *   up axis
1097 *
1098 * Clients use this to set the "up" direction for all volumes.  Volumes
1099 * are oriented such that this direction points upward.
1100 * ----------------------------------------------------------------------
1101 */
1102static int
1103UpCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1104{
1105    if (objc != 2) {
1106        Tcl_AppendResult(interp, "wrong # args: should be \"",
1107                         Tcl_GetString(objv[0]), " x|y|z|-x|-y|-z\"", (char*)NULL);
1108        return TCL_ERROR;
1109    }
1110
1111    int sign;
1112    int axis;
1113    if (GetAxisDirFromObj(interp, objv[1], &axis, &sign) != TCL_OK) {
1114        return TCL_ERROR;
1115    }
1116    NanoVis::updir = (axis+1)*sign;
1117    return TCL_OK;
1118}
1119
1120
1121static int
1122VolumeAnimationCaptureOp(ClientData cdata, Tcl_Interp *interp, int objc,
1123                         Tcl_Obj *const *objv)
1124{
1125    int total;
1126    if (Tcl_GetIntFromObj(interp, objv[3], &total) != TCL_OK) {
1127        return TCL_ERROR;
1128    }
1129    VolumeInterpolator* interpolator;
1130    interpolator = NanoVis::vol_renderer->getVolumeInterpolator();
1131    interpolator->start();
1132    if (interpolator->is_started()) {
1133        const char *fileName = (objc < 5) ? NULL : Tcl_GetString(objv[4]);
1134        for (int frame_num = 0; frame_num < total; ++frame_num) {
1135            float fraction;
1136
1137            fraction = ((float)frame_num) / (total - 1);
1138            Trace("fraction : %f\n", fraction);
1139            //interpolator->update(((float)frame_num) / (total - 1));
1140            interpolator->update(fraction);
1141
1142            NanoVis::offscreen_buffer_capture();  //enable offscreen render
1143
1144            NanoVis::display();
1145            NanoVis::read_screen();
1146
1147            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1148
1149            NanoVis::bmp_write_to_file(frame_num, fileName);
1150        }
1151    }
1152    return TCL_OK;
1153}
1154
1155static int
1156VolumeAnimationClearOp(ClientData cdata, Tcl_Interp *interp, int objc,
1157                       Tcl_Obj *const *objv)
1158{
1159    NanoVis::vol_renderer->clearAnimatedVolumeInfo();
1160    return TCL_OK;
1161}
1162
1163static int
1164VolumeAnimationStartOp(ClientData cdata, Tcl_Interp *interp, int objc,
1165                       Tcl_Obj *const *objv)
1166{
1167    NanoVis::vol_renderer->startVolumeAnimation();
1168    return TCL_OK;
1169}
1170
1171static int
1172VolumeAnimationStopOp(ClientData cdata, Tcl_Interp *interp, int objc,
1173                      Tcl_Obj *const *objv)
1174{
1175    NanoVis::vol_renderer->stopVolumeAnimation();
1176    return TCL_OK;
1177}
1178
1179static int
1180VolumeAnimationVolumesOp(ClientData cdata, Tcl_Interp *interp, int objc,
1181                         Tcl_Obj *const *objv)
1182{
1183    vector<unsigned int> ivol;
1184    if (GetVolumeIndices(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
1185        return TCL_ERROR;
1186    }
1187    Trace("parsing volume index\n");
1188    vector<unsigned int>::iterator iter;
1189    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1190        Trace("index: %d\n", *iter);
1191        NanoVis::vol_renderer->addAnimatedVolume(NanoVis::volume[*iter], *iter);
1192    }
1193    return TCL_OK;
1194}
1195
1196static Rappture::CmdSpec volumeAnimationOps[] = {
1197    {"capture",   2, VolumeAnimationCaptureOp,  4, 5, "numframes ?filename?",},
1198    {"clear",     2, VolumeAnimationClearOp,    3, 3, "",},
1199    {"start",     3, VolumeAnimationStartOp,    3, 3, "",},
1200    {"stop",      3, VolumeAnimationStopOp,     3, 3, "",},
1201    {"volumes",   1, VolumeAnimationVolumesOp,  3, 0, "?indices?",},
1202};
1203
1204static int nVolumeAnimationOps = NumCmdSpecs(volumeAnimationOps);
1205
1206static int
1207VolumeAnimationOp(ClientData cdata, Tcl_Interp *interp, int objc,
1208                  Tcl_Obj *const *objv)
1209{
1210    Tcl_ObjCmdProc *proc;
1211
1212    proc = Rappture::GetOpFromObj(interp, nVolumeAnimationOps, volumeAnimationOps,
1213                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1214    if (proc == NULL) {
1215        return TCL_ERROR;
1216    }
1217    return (*proc) (cdata, interp, objc, objv);
1218}
1219
1220
1221static int
1222VolumeDataFollowsOp(ClientData cdata, Tcl_Interp *interp, int objc,
1223                    Tcl_Obj *const *objv)
1224{
1225    printf("Data Loading\n");
1226    fflush(stdout);
1227
1228    int nbytes;
1229    if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
1230        return TCL_ERROR;
1231    }
1232
1233    Rappture::Buffer buf;
1234    if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
1235        return TCL_ERROR;
1236    }
1237    int n = NanoVis::n_volumes;
1238    char header[6];
1239    memcpy(header, buf.bytes(), sizeof(char) * 5);
1240    header[5] = '\0';
1241
1242#if _LOCAL_ZINC_TEST_
1243    //FILE* fp = fopen("/home/nanohub/vrinside/nv/data/HOON/QDWL_100_100_50_strain_8000i.nd_zatom_12_1", "rb");
1244    FILE* fp;
1245
1246    fp = fopen("/home/nanohub/vrinside/nv/data/HOON/GaAs_AlGaAs_2QD_B4.nd_zc_1_wf", "rb");
1247    if (fp == NULL) {
1248        printf("cannot open the file\n");
1249        fflush(stdout);
1250        return TCL_ERROR;
1251    }
1252    unsigned char* b = (unsigned char*)malloc(buf.size());
1253    fread(b, buf.size(), 1, fp);
1254    fclose(fp);
1255#endif  /*_LOCAL_ZINC_TEST_*/
1256    printf("Checking header[%s]\n", header);
1257    fflush(stdout);
1258    if (strcmp(header, "<HDR>") == 0) {
1259        Volume* vol = NULL;
1260
1261        printf("ZincBlende stream is in\n");
1262        fflush(stdout);
1263        //std::stringstream fdata(std::ios_base::out|std::ios_base::in|std::ios_base::binary);
1264        //fdata.write(buf.bytes(),buf.size());
1265        //vol = NvZincBlendeReconstructor::getInstance()->loadFromStream(fdata);
1266
1267#if _LOCAL_ZINC_TEST_
1268        vol = NvZincBlendeReconstructor::getInstance()->loadFromMemory(b);
1269#else
1270        vol = NvZincBlendeReconstructor::getInstance()->loadFromMemory((void*) buf.bytes());
1271#endif  /*_LOCAL_ZINC_TEST_*/
1272        if (vol == NULL) {
1273            Tcl_AppendResult(interp, "can't get volume instance", (char *)NULL);
1274            return TCL_OK;
1275        }
1276        printf("finish loading\n");
1277        fflush(stdout);
1278        while (NanoVis::n_volumes <= n) {
1279            NanoVis::volume.push_back((Volume*) NULL);
1280            NanoVis::n_volumes++;
1281        }
1282
1283        if (NanoVis::volume[n] != NULL) {
1284            delete NanoVis::volume[n];
1285            NanoVis::volume[n] = NULL;
1286        }
1287
1288        float dx0 = -0.5;
1289        float dy0 = -0.5*vol->height/vol->width;
1290        float dz0 = -0.5*vol->depth/vol->width;
1291        vol->move(Vector3(dx0, dy0, dz0));
1292
1293        NanoVis::volume[n] = vol;
1294#if __TEST_CODE__
1295    } else if (strcmp(header, "<FET>") == 0) {
1296        printf("FET loading...\n");
1297        fflush(stdout);
1298        std::stringstream fdata;
1299        fdata.write(buf.bytes(),buf.size());
1300        err = load_volume_stream3(n, fdata);
1301        if (err) {
1302            Tcl_AppendResult(interp, err.remark().c_str(), (char*)NULL);
1303            return TCL_ERROR;
1304        }
1305#endif  /*__TEST_CODE__*/
1306    } else if (strcmp(header, "<ODX>") == 0) {
1307        Rappture::Outcome err;
1308
1309        printf("Loading DX using OpenDX library...\n");
1310        fflush(stdout);
1311        err = load_volume_stream_odx(n, buf.bytes()+5, buf.size()-5);
1312        if (err) {
1313            Tcl_AppendResult(interp, err.remark().c_str(), (char*)NULL);
1314            return TCL_ERROR;
1315        }
1316    } else {
1317        Rappture::Outcome err;
1318
1319        printf("OpenDX loading...\n");
1320        fflush(stdout);
1321        std::stringstream fdata;
1322        fdata.write(buf.bytes(),buf.size());
1323
1324#if ISO_TEST
1325        err = load_volume_stream2(n, fdata);
1326#else
1327        err = load_volume_stream(n, fdata);
1328#endif
1329        if (err) {
1330            Tcl_AppendResult(interp, err.remark().c_str(), (char*)NULL);
1331            return TCL_ERROR;
1332        }
1333    }
1334
1335    //
1336    // BE CAREFUL: Set the number of slices to something slightly different
1337    // for each volume.  If we have identical volumes at exactly the same
1338    // position with exactly the same number of slices, the second volume will
1339    // overwrite the first, so the first won't appear at all.
1340    //
1341    if (NanoVis::volume[n] != NULL) {
1342        //NanoVis::volume[n]->set_n_slice(512-n);
1343        NanoVis::volume[n]->set_n_slice(256-n);
1344        NanoVis::volume[n]->disable_cutplane(0);
1345        NanoVis::volume[n]->disable_cutplane(1);
1346        NanoVis::volume[n]->disable_cutplane(2);
1347
1348        NanoVis::vol_renderer->add_volume(NanoVis::volume[n],
1349                                          NanoVis::get_transfunc("default"));
1350    }
1351
1352    {
1353        Volume *volPtr;
1354        char info[1024];
1355
1356        if (Volume::update_pending) {
1357            NanoVis::SetVolumeRanges();
1358        }
1359        volPtr = NanoVis::volume[n];
1360        sprintf(info, "nv>data id %d min %g max %g vmin %g vmax %g\n",
1361                n, volPtr->wAxis.min(), volPtr->wAxis.max(),
1362                Volume::valueMin, Volume::valueMax);
1363        write(0, info, strlen(info));
1364    }
1365    return TCL_OK;
1366}
1367
1368static int
1369VolumeDataStateOp(ClientData cdata, Tcl_Interp *interp, int objc,
1370                  Tcl_Obj *const *objv)
1371{
1372    bool state;
1373    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1374        return TCL_ERROR;
1375    }
1376    vector<Volume *> ivol;
1377    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1378        return TCL_ERROR;
1379    }
1380    if (state) {
1381        vector<Volume *>::iterator iter;
1382        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1383            (*iter)->enable_data();
1384        }
1385    } else {
1386        vector<Volume *>::iterator iter;
1387        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1388            (*iter)->disable_data();
1389        }
1390    }
1391    return TCL_OK;
1392}
1393
1394static Rappture::CmdSpec volumeDataOps[] = {
1395    {"follows",   1, VolumeDataFollowsOp, 4, 4, "size",},
1396    {"state",     1, VolumeDataStateOp,   4, 0, "bool ?indices?",},
1397};
1398static int nVolumeDataOps = NumCmdSpecs(volumeDataOps);
1399
1400static int
1401VolumeDataOp(ClientData cdata, Tcl_Interp *interp, int objc,
1402             Tcl_Obj *const *objv)
1403{
1404    Tcl_ObjCmdProc *proc;
1405
1406    proc = Rappture::GetOpFromObj(interp, nVolumeDataOps, volumeDataOps,
1407                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1408    if (proc == NULL) {
1409        return TCL_ERROR;
1410    }
1411    return (*proc) (cdata, interp, objc, objv);
1412}
1413
1414static int
1415VolumeOutlineColorOp(ClientData cdata, Tcl_Interp *interp, int objc,
1416                     Tcl_Obj *const *objv)
1417{
1418    float rgb[3];
1419    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1420        return TCL_ERROR;
1421    }
1422    vector<Volume *> ivol;
1423    if (GetVolumes(interp, objc - 6, objv + 6, &ivol) != TCL_OK) {
1424        return TCL_ERROR;
1425    }
1426    vector<Volume *>::iterator iter;
1427    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1428        (*iter)->set_outline_color(rgb);
1429    }
1430    return TCL_OK;
1431}
1432
1433static int
1434VolumeOutlineStateOp(ClientData cdata, Tcl_Interp *interp, int objc,
1435                     Tcl_Obj *const *objv)
1436{
1437    bool state;
1438    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1439        return TCL_ERROR;
1440    }
1441    vector<Volume *> ivol;
1442    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1443        return TCL_ERROR;
1444    }
1445    if (state) {
1446        vector<Volume *>::iterator iter;
1447        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1448            (*iter)->enable_outline();
1449        }
1450    } else {
1451        vector<Volume *>::iterator iter;
1452        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1453            (*iter)->disable_outline();
1454        }
1455    }
1456    return TCL_OK;
1457}
1458
1459
1460static Rappture::CmdSpec volumeOutlineOps[] = {
1461    {"color",     1, VolumeOutlineColorOp,  6, 0, "r g b ?indices?",},
1462    {"state",     1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1463    {"visible",   1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1464};
1465static int nVolumeOutlineOps = NumCmdSpecs(volumeOutlineOps);
1466
1467static int
1468VolumeOutlineOp(ClientData cdata, Tcl_Interp *interp, int objc,
1469                Tcl_Obj *const *objv)
1470{
1471    Tcl_ObjCmdProc *proc;
1472
1473    proc = Rappture::GetOpFromObj(interp, nVolumeOutlineOps, volumeOutlineOps,
1474        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1475    if (proc == NULL) {
1476        return TCL_ERROR;
1477    }
1478    return (*proc) (cdata, interp, objc, objv);
1479}
1480
1481static int
1482VolumeShadingDiffuseOp(ClientData cdata, Tcl_Interp *interp, int objc,
1483                       Tcl_Obj *const *objv)
1484{
1485    float diffuse;
1486    if (GetFloatFromObj(interp, objv[3], &diffuse) != TCL_OK) {
1487        return TCL_ERROR;
1488    }
1489
1490    vector<Volume *> ivol;
1491    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1492        return TCL_ERROR;
1493    }
1494    vector<Volume *>::iterator iter;
1495    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1496        (*iter)->set_diffuse(diffuse);
1497    }
1498    return TCL_OK;
1499}
1500
1501static int
1502VolumeShadingIsosurfaceOp(ClientData cdata, Tcl_Interp *interp, int objc,
1503                          Tcl_Obj *const *objv)
1504{
1505    bool iso_surface;
1506    if (GetBooleanFromObj(interp, objv[3], &iso_surface) != TCL_OK) {
1507        return TCL_ERROR;
1508    }
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_isosurface(iso_surface);
1516    }
1517    return TCL_OK;
1518}
1519
1520static int
1521VolumeShadingOpacityOp(ClientData cdata, Tcl_Interp *interp, int objc,
1522                       Tcl_Obj *const *objv)
1523{
1524
1525    float opacity;
1526    if (GetFloatFromObj(interp, objv[3], &opacity) != TCL_OK) {
1527        return TCL_ERROR;
1528    }
1529    printf("set opacity %f\n", opacity);
1530    vector<Volume *> ivol;
1531    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1532        return TCL_ERROR;
1533    }
1534    vector<Volume *>::iterator iter;
1535    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1536        (*iter)->set_opacity_scale(opacity);
1537    }
1538    return TCL_OK;
1539}
1540
1541static int
1542VolumeShadingSpecularOp(ClientData cdata, Tcl_Interp *interp, int objc,
1543                        Tcl_Obj *const *objv)
1544{
1545    float specular;
1546    if (GetFloatFromObj(interp, objv[3], &specular) != TCL_OK) {
1547        return TCL_ERROR;
1548    }
1549    vector<Volume *> ivol;
1550    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1551        return TCL_ERROR;
1552    }
1553    vector<Volume *>::iterator iter;
1554    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1555        (*iter)->set_specular(specular);
1556    }
1557    return TCL_OK;
1558}
1559
1560static int
1561VolumeShadingTransFuncOp(ClientData cdata, Tcl_Interp *interp, int objc,
1562                         Tcl_Obj *const *objv)
1563{
1564    TransferFunction *tf;
1565    const char *name = Tcl_GetString(objv[3]);
1566    tf = NanoVis::get_transfunc(name);
1567    if (tf == NULL) {
1568        Tcl_AppendResult(interp, "transfer function \"", name,
1569                         "\" is not defined", (char*)NULL);
1570        return TCL_ERROR;
1571    }
1572    vector<Volume *> ivol;
1573    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1574        return TCL_ERROR;
1575    }
1576    vector<Volume *>::iterator iter;
1577    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1578        NanoVis::vol_renderer->shade_volume(*iter, tf);
1579#ifdef POINTSET
1580        // TBD..
1581        // POINTSET
1582        if ((*iter)->pointsetIndex != -1) {
1583            g_pointSet[(*iter)->pointsetIndex]->updateColor(tf->getData(), 256);
1584        }
1585#endif /*POINTSET*/
1586    }
1587    return TCL_OK;
1588}
1589
1590static Rappture::CmdSpec volumeShadingOps[] = {
1591    {"diffuse",     1, VolumeShadingDiffuseOp,    4, 0, "value ?indices?",},
1592    {"isosurface",  1, VolumeShadingIsosurfaceOp, 4, 0, "bool ?indices?",},
1593    {"opacity",     1, VolumeShadingOpacityOp,    4, 0, "value ?indices?",},
1594    {"specular",    1, VolumeShadingSpecularOp,   4, 0, "value ?indices?",},
1595    {"transfunc",   1, VolumeShadingTransFuncOp,  4, 0, "funcName ?indices?",},
1596};
1597static int nVolumeShadingOps = NumCmdSpecs(volumeShadingOps);
1598
1599static int
1600VolumeShadingOp(ClientData cdata, Tcl_Interp *interp, int objc,
1601                Tcl_Obj *const *objv)
1602{
1603    Tcl_ObjCmdProc *proc;
1604
1605    proc = Rappture::GetOpFromObj(interp, nVolumeShadingOps, volumeShadingOps,
1606        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1607    if (proc == NULL) {
1608        return TCL_ERROR;
1609    }
1610    return (*proc) (cdata, interp, objc, objv);
1611}
1612
1613static int
1614VolumeAxisOp(ClientData cdata, Tcl_Interp *interp, int objc,
1615             Tcl_Obj *const *objv)
1616{
1617    const char *string = Tcl_GetString(objv[2]);
1618    char c;
1619    c = string[0];
1620    if ((c == 'l') && (strcmp(string, "label") == 0)) {
1621        int axis;
1622        if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
1623            return TCL_ERROR;
1624        }
1625        vector<Volume *> ivol;
1626        if (GetVolumes(interp, objc - 5, objv + 5, &ivol) != TCL_OK) {
1627            return TCL_ERROR;
1628        }
1629        vector<Volume *>::iterator iter;
1630        const char *label;
1631        label = Tcl_GetString(objv[4]);
1632        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1633            (*iter)->set_label(axis, label);
1634        }
1635    } else {
1636        Tcl_AppendResult(interp, "bad option \"", string,
1637                         "\": should be label", (char*)NULL);
1638        return TCL_ERROR;
1639    }
1640    return TCL_OK;
1641}
1642
1643static int
1644VolumeStateOp(ClientData cdata, Tcl_Interp *interp, int objc,
1645              Tcl_Obj *const *objv)
1646{
1647    bool state;
1648    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1649        return TCL_ERROR;
1650    }
1651    vector<Volume *> ivol;
1652    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
1653        return TCL_ERROR;
1654    }
1655    if (state) {
1656        vector<Volume *>::iterator iter;
1657        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1658            (*iter)->enable();
1659        }
1660    } else {
1661        vector<Volume *>::iterator iter;
1662        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1663            (*iter)->disable();
1664        }
1665    }
1666    return TCL_OK;
1667}
1668
1669static int
1670VolumeTestOp(ClientData cdata, Tcl_Interp *interp, int objc,
1671             Tcl_Obj *const *objv)
1672{
1673    NanoVis::volume[1]->disable_data();
1674    NanoVis::volume[1]->disable();
1675    return TCL_OK;
1676}
1677
1678static Rappture::CmdSpec volumeOps[] = {
1679    {"animation", 2, VolumeAnimationOp,   3, 0, "oper ?args?",},
1680    {"axis",      2, VolumeAxisOp,        4, 0, "label axis value ?indices?",},
1681    {"data",      1, VolumeDataOp,        3, 0, "oper ?args?",},
1682    {"outline",   1, VolumeOutlineOp,     3, 0, "oper ?args?",},
1683    {"shading",   2, VolumeShadingOp,     3, 0, "oper ?args?",},
1684    {"state",     2, VolumeStateOp,       3, 0, "bool ?indices?",},
1685    {"test2",     1, VolumeTestOp,        2, 2, "",},
1686};
1687static int nVolumeOps = NumCmdSpecs(volumeOps);
1688
1689/*
1690 * ----------------------------------------------------------------------
1691 * CLIENT COMMAND:
1692 *   volume axis label x|y|z <value> ?<volumeId> ...?
1693 *   volume data state on|off ?<volumeId> ...?
1694 *   volume outline state on|off ?<volumeId> ...?
1695 *   volume outline color on|off ?<volumeId> ...?
1696 *   volume shading transfunc <name> ?<volumeId> ...?
1697 *   volume shading diffuse <value> ?<volumeId> ...?
1698 *   volume shading specular <value> ?<volumeId> ...?
1699 *   volume shading opacity <value> ?<volumeId> ...?
1700 *   volume state on|off ?<volumeId> ...?
1701 *
1702 * Clients send these commands to manipulate the volumes.
1703 * ----------------------------------------------------------------------
1704 */
1705static int
1706VolumeCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1707{
1708    Tcl_ObjCmdProc *proc;
1709
1710    proc = Rappture::GetOpFromObj(interp, nVolumeOps, volumeOps,
1711        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1712    if (proc == NULL) {
1713        return TCL_ERROR;
1714    }
1715    return (*proc) (cdata, interp, objc, objv);
1716}
1717
1718static int
1719FlowCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1720{
1721    Rappture::Outcome err;
1722
1723    if (objc < 2) {
1724        Tcl_AppendResult(interp, "wrong # args: should be \"",
1725                Tcl_GetString(objv[0]), " option ?arg arg?", (char *)NULL);
1726        return TCL_ERROR;
1727    }
1728    const char *string = Tcl_GetString(objv[1]);
1729    char c = string[0];
1730    if ((c == 'v') && (strcmp(string, "vectorid") == 0)) {
1731        if (objc != 3) {
1732            Tcl_AppendResult(interp, "wrong # args: should be \"",
1733                Tcl_GetString(objv[0]), " vectorid volume", (char *)NULL);
1734            return TCL_ERROR;
1735        }
1736        Volume *volPtr;
1737        if (GetVolumeFromObj(interp, objv[2], &volPtr) != TCL_OK) {
1738            return TCL_ERROR;
1739        }
1740        if (NanoVis::particleRenderer != NULL) {
1741            NanoVis::particleRenderer->setVectorField(volPtr->id, 1.0f,
1742                volPtr->height / (float)volPtr->width,
1743                volPtr->depth  / (float)volPtr->width,
1744                volPtr->wAxis.max());
1745            NanoVis::initParticle();
1746        }
1747        if (NanoVis::licRenderer != NULL) {
1748            NanoVis::licRenderer->setVectorField(volPtr->id,
1749                1.0f / volPtr->aspect_ratio_width,
1750                1.0f / volPtr->aspect_ratio_height,
1751                1.0f / volPtr->aspect_ratio_depth,
1752                volPtr->wAxis.max());
1753            NanoVis::licRenderer->set_offset(NanoVis::lic_slice_z);
1754        }
1755    } else if (c == 'l' && strcmp(string, "lic") == 0) {
1756        if (objc != 3) {
1757            Tcl_AppendResult(interp, "wrong # args: should be \"",
1758                             Tcl_GetString(objv[0]), " lic on|off\"", (char*)NULL);
1759            return TCL_ERROR;
1760        }
1761        bool state;
1762        if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1763            return TCL_ERROR;
1764        }
1765        NanoVis::lic_on = state;
1766    } else if ((c == 'p') && (strcmp(string, "particle") == 0)) {
1767        if (objc < 3) {
1768            Tcl_AppendResult(interp, "wrong # args: should be \"",
1769                Tcl_GetString(objv[0]), " particle visible|slice|slicepos arg \"",
1770                (char*)NULL);
1771            return TCL_ERROR;
1772        }
1773        const char *string = Tcl_GetString(objv[2]);
1774        c = string[0];
1775        if ((c == 'v') && (strcmp(string, "visible") == 0)) {
1776            if (objc != 4) {
1777                Tcl_AppendResult(interp, "wrong # args: should be \"",
1778                                 Tcl_GetString(objv[0]), " particle visible on|off\"",
1779                                 (char*)NULL);
1780                return TCL_ERROR;
1781            }
1782            bool state;
1783            if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1784                return TCL_ERROR;
1785            }
1786            NanoVis::particle_on = state;
1787        } else if ((c == 's') && (strcmp(string, "slice") == 0)) {
1788            if (objc != 4) {
1789                Tcl_AppendResult(interp, "wrong # args: should be \"",
1790                                 Tcl_GetString(objv[0]),
1791                                 " particle slice volume\"", (char*)NULL);
1792                return TCL_ERROR;
1793            }
1794            int axis;
1795            if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
1796                return TCL_ERROR;
1797            }
1798            NanoVis::lic_axis = axis;
1799        } else if ((c == 's') && (strcmp(string, "slicepos") == 0)) {
1800            if (objc != 4) {
1801                Tcl_AppendResult(interp, "wrong # args: should be \"",
1802                                 Tcl_GetString(objv[0]), " particle slicepos value\"",
1803                                 (char*)NULL);
1804                return TCL_ERROR;
1805            }
1806            float pos;
1807            if (GetFloatFromObj(interp, objv[2], &pos) != TCL_OK) {
1808                return TCL_ERROR;
1809            }
1810            if (pos < 0.0f) {
1811                pos = 0.0f;
1812            } else if (pos > 1.0f) {
1813                pos = 1.0f;
1814            }
1815            switch (NanoVis::lic_axis) {
1816            case 0 :
1817                NanoVis::lic_slice_x = pos;
1818                break;
1819            case 1 :
1820                NanoVis::lic_slice_y = pos;
1821                break;
1822            case 2 :
1823                NanoVis::lic_slice_z = pos;
1824                break;
1825            }
1826        } else {
1827            Tcl_AppendResult(interp, "unknown option \"", string,
1828                "\": should be \"", Tcl_GetString(objv[0]),
1829                " visible, slice, or slicepos\"", (char *)NULL);
1830            return TCL_ERROR;
1831        }
1832    } else if ((c == 'r') && (strcmp(string, "reset") == 0)) {
1833        NanoVis::initParticle();
1834    } else if ((c == 'c') && (strcmp(string, "capture") == 0)) {
1835        if (objc > 4 || objc < 3) {
1836            Tcl_AppendResult(interp, "wrong # args: should be \"",
1837                             Tcl_GetString(objv[0]), " capture numframes [directory]\"",
1838                             (char*)NULL);
1839            return TCL_ERROR;
1840        }
1841        int total_frame_count;
1842
1843        if (Tcl_GetIntFromObj(interp, objv[2], &total_frame_count) != TCL_OK) {
1844            return TCL_ERROR;
1845        }
1846        if (NanoVis::licRenderer) {
1847            NanoVis::licRenderer->activate();
1848        }
1849        if (NanoVis::particleRenderer) {
1850            NanoVis::particleRenderer->activate();
1851        }
1852        // Karl
1853        //
1854        Trace("FLOW started\n");
1855        const char *fileName;
1856        fileName = (objc < 4) ? NULL : Tcl_GetString(objv[3]);
1857        for (int frame_count = 0; frame_count < total_frame_count;
1858             frame_count++) {
1859
1860            // Generate the latest frame and send it back to the client
1861            if (NanoVis::licRenderer &&
1862                NanoVis::licRenderer->isActivated()) {
1863                NanoVis::licRenderer->convolve();
1864            }
1865            if (NanoVis::particleRenderer &&
1866                NanoVis::particleRenderer->isActivated()) {
1867                NanoVis::particleRenderer->advect();
1868            }
1869            NanoVis::offscreen_buffer_capture();  //enable offscreen render
1870            NanoVis::display();
1871
1872            //          printf("Read Screen for Writing to file...\n");
1873
1874            NanoVis::read_screen();
1875            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1876
1877            NanoVis::bmp_write_to_file(frame_count, fileName);
1878        }
1879        Trace("FLOW end\n");
1880        // put your code...
1881        if (NanoVis::licRenderer) {
1882            NanoVis::licRenderer->deactivate();
1883        }
1884        if (NanoVis::particleRenderer) {
1885            NanoVis::particleRenderer->deactivate();
1886        }
1887        NanoVis::initParticle();
1888    } else if ((c == 'd') && (strcmp(string, "data") == 0)) {
1889        if (objc < 3) {
1890            Tcl_AppendResult(interp, "wrong # args: should be \"",
1891                Tcl_GetString(objv[0]), " data follows ?args?", (char *)NULL);
1892            return TCL_ERROR;
1893        }
1894        const char *string = Tcl_GetString(objv[2]);;
1895        c = string[0];
1896        if ((c == 'f') && (strcmp(string,"follows") == 0)) {
1897            if (objc != 4) {
1898                Tcl_AppendResult(interp, "wrong # args: should be \"",
1899                    Tcl_GetString(objv[0]), " data follows length",
1900                    (char *)NULL);
1901                return TCL_ERROR;
1902            }
1903            int nbytes;
1904            if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
1905                return TCL_ERROR;
1906            }
1907            Rappture::Buffer buf;
1908            if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
1909                return TCL_ERROR;
1910            }
1911            int n = NanoVis::n_volumes;
1912            std::stringstream fdata;
1913            fdata.write(buf.bytes(),buf.size());
1914            load_vector_stream(n, fdata);
1915            Volume *volPtr = NanoVis::volume[n];
1916
1917            //
1918            // BE CAREFUL:  Set the number of slices to something
1919            //   slightly different for each volume.  If we have
1920            //   identical volumes at exactly the same position
1921            //   with exactly the same number of slices, the second
1922            //   volume will overwrite the first, so the first won't
1923            //   appear at all.
1924            //
1925            if (volPtr != NULL) {
1926                //volPtr->set_n_slice(256-n);
1927                volPtr->set_n_slice(512-n);
1928                volPtr->disable_cutplane(0);
1929                volPtr->disable_cutplane(1);
1930                volPtr->disable_cutplane(2);
1931
1932                NanoVis::vol_renderer->add_volume(volPtr,
1933                        NanoVis::get_transfunc("default"));
1934            }
1935        }
1936    } else {
1937        return TCL_ERROR;
1938    }
1939    return TCL_OK;
1940}
1941
1942static int
1943HeightMapDataFollowsOp(ClientData cdata, Tcl_Interp *interp, int objc,
1944                       Tcl_Obj *const *objv)
1945{
1946    Rappture::Buffer buf;
1947    int nBytes;
1948
1949    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
1950        return TCL_ERROR;
1951    }
1952    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
1953        return TCL_ERROR;
1954    }
1955    buf.append("\0", 1);
1956    int result;
1957    result = Tcl_Eval(interp, buf.bytes());
1958    if (result != TCL_OK) {
1959        fprintf(NanoVis::logfile, "error in command: %s\n",
1960                Tcl_GetStringResult(interp));
1961        fflush(NanoVis::logfile);
1962    }
1963    return result;
1964}
1965
1966static int
1967HeightMapDataVisibleOp(ClientData cdata, Tcl_Interp *interp, int objc,
1968                       Tcl_Obj *const *objv)
1969{
1970    bool visible;
1971    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1972        return TCL_ERROR;
1973    }
1974    vector<HeightMap *> imap;
1975    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1976        return TCL_ERROR;
1977    }
1978    vector<HeightMap *>::iterator iter;
1979    for (iter = imap.begin(); iter != imap.end(); iter++) {
1980        (*iter)->setVisible(visible);
1981    }
1982    return TCL_OK;
1983}
1984
1985static Rappture::CmdSpec heightMapDataOps[] = {
1986    {"follows",      1, HeightMapDataFollowsOp, 4, 4, "length",},
1987    {"visible",      1, HeightMapDataVisibleOp, 4, 0, "bool ?indices?",},
1988};
1989static int nHeightMapDataOps = NumCmdSpecs(heightMapDataOps);
1990
1991static int
1992HeightMapDataOp(ClientData cdata, Tcl_Interp *interp, int objc,
1993                Tcl_Obj *const *objv)
1994{
1995    Tcl_ObjCmdProc *proc;
1996
1997    proc = Rappture::GetOpFromObj(interp, nHeightMapDataOps, heightMapDataOps,
1998                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1999    if (proc == NULL) {
2000        return TCL_ERROR;
2001    }
2002    return (*proc) (cdata, interp, objc, objv);
2003}
2004
2005
2006static int
2007HeightMapLineContourColorOp(ClientData cdata, Tcl_Interp *interp, int objc,
2008                            Tcl_Obj *const *objv)
2009{
2010    float rgb[3];
2011    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
2012        return TCL_ERROR;
2013    }
2014    vector<HeightMap *> imap;
2015    if (GetHeightMaps(interp, objc - 6, objv + 6, &imap) != TCL_OK) {
2016        return TCL_ERROR;
2017    }
2018    vector<HeightMap *>::iterator iter;
2019    for (iter = imap.begin(); iter != imap.end(); iter++) {
2020        (*iter)->setLineContourColor(rgb);
2021    }
2022    return TCL_OK;
2023}
2024
2025static int
2026HeightMapLineContourVisibleOp(ClientData cdata, Tcl_Interp *interp, int objc,
2027                              Tcl_Obj *const *objv)
2028{
2029    bool visible;
2030    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
2031        return TCL_ERROR;
2032    }
2033    vector<HeightMap *> imap;
2034    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
2035        return TCL_ERROR;
2036    }
2037    vector<HeightMap *>::iterator iter;
2038    for (iter = imap.begin(); iter != imap.end(); iter++) {
2039        (*iter)->setLineContourVisible(visible);
2040    }
2041    return TCL_OK;
2042}
2043
2044static Rappture::CmdSpec heightMapLineContourOps[] = {
2045    {"color",   1, HeightMapLineContourColorOp,   4, 4, "length",},
2046    {"visible", 1, HeightMapLineContourVisibleOp, 4, 0, "bool ?indices?",},
2047};
2048static int nHeightMapLineContourOps = NumCmdSpecs(heightMapLineContourOps);
2049
2050static int
2051HeightMapLineContourOp(ClientData cdata, Tcl_Interp *interp, int objc,
2052                       Tcl_Obj *const *objv)
2053{
2054    Tcl_ObjCmdProc *proc;
2055
2056    proc = Rappture::GetOpFromObj(interp, nHeightMapLineContourOps,
2057        heightMapLineContourOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
2058    if (proc == NULL) {
2059        return TCL_ERROR;
2060    }
2061    return (*proc) (cdata, interp, objc, objv);
2062}
2063
2064static int
2065HeightMapCullOp(ClientData cdata, Tcl_Interp *interp, int objc,
2066                Tcl_Obj *const *objv)
2067{
2068    graphics::RenderContext::CullMode mode;
2069    if (GetCullMode(interp, objv[2], &mode) != TCL_OK) {
2070        return TCL_ERROR;
2071    }
2072    NanoVis::renderContext->setCullMode(mode);
2073    return TCL_OK;
2074}
2075
2076static int
2077HeightMapCreateOp(ClientData cdata, Tcl_Interp *interp, int objc,
2078                  Tcl_Obj *const *objv)
2079{
2080    HeightMap *hmPtr;
2081
2082    /* heightmap create xmin ymin xmax ymax xnum ynum values */
2083    hmPtr = CreateHeightMap(cdata, interp, objc - 2, objv + 2);
2084    if (hmPtr == NULL) {
2085        return TCL_ERROR;
2086    }
2087    NanoVis::heightMap.push_back(hmPtr);
2088    Tcl_SetIntObj(Tcl_GetObjResult(interp), NanoVis::heightMap.size() - 1);;
2089    return TCL_OK;
2090}
2091
2092static int
2093HeightMapLegendOp(ClientData cdata, Tcl_Interp *interp, int objc,
2094                  Tcl_Obj *const *objv)
2095{
2096    HeightMap *hmPtr;
2097    if (GetHeightMapFromObj(interp, objv[2], &hmPtr) != TCL_OK) {
2098        return TCL_ERROR;
2099    }
2100    TransferFunction *tf;
2101    tf = hmPtr->getColorMap();
2102    if (tf == NULL) {
2103        Tcl_AppendResult(interp, "no transfer function defined for heightmap \"",
2104                         Tcl_GetString(objv[2]), "\"", (char*)NULL);
2105        return TCL_ERROR;
2106    }
2107    int w, h;
2108    if ((Tcl_GetIntFromObj(interp, objv[3], &w) != TCL_OK) ||
2109        (Tcl_GetIntFromObj(interp, objv[4], &h) != TCL_OK)) {
2110        return TCL_ERROR;
2111    }
2112    if (HeightMap::update_pending) {
2113        NanoVis::SetHeightmapRanges();
2114    }
2115    NanoVis::render_legend(tf, HeightMap::valueMin, HeightMap::valueMax, w, h,
2116        "label");
2117    return TCL_OK;
2118}
2119
2120static int
2121HeightMapPolygonOp(ClientData cdata, Tcl_Interp *interp, int objc,
2122                   Tcl_Obj *const *objv)
2123{
2124    graphics::RenderContext::PolygonMode mode;
2125    if (GetPolygonMode(interp, objv[2], &mode) != TCL_OK) {
2126        return TCL_ERROR;
2127    }
2128    NanoVis::renderContext->setPolygonMode(mode);
2129    return TCL_OK;
2130}
2131
2132static int
2133HeightMapShadingOp(ClientData cdata, Tcl_Interp *interp, int objc,
2134                 Tcl_Obj *const *objv)
2135{
2136    graphics::RenderContext::ShadingModel model;
2137    if (GetShadingModel(interp, objv[2], &model) != TCL_OK) {
2138        return TCL_ERROR;
2139    }
2140    NanoVis::renderContext->setShadingModel(model);
2141    return TCL_OK;
2142}
2143
2144
2145static int
2146HeightMapTopView(ClientData data, Tcl_Interp *interp, int objc,
2147                Tcl_Obj *const *objv)
2148{
2149
2150    // the variables below should be reassigned
2151    int image_width = 512;
2152    int image_height = 512;
2153    HeightMap* heightmap = 0;
2154
2155    // HELP ME
2156    // GEORGE
2157
2158    NanoVis::render_2d_contour(heightmap, image_width, image_height);
2159   
2160    return TCL_OK;
2161}
2162
2163static int
2164HeightMapTestOp(ClientData cdata, Tcl_Interp *interp, int objc,
2165                Tcl_Obj *const *objv)
2166{
2167    srand((unsigned)time(NULL));
2168
2169    int size = 20 * 20;
2170    double sigma = 5.0;
2171    double mean = exp(0.0) / (sigma * sqrt(2.0));
2172    float* data = (float*) malloc(sizeof(float) * size);
2173
2174    float x, y;
2175    for (int i = 0; i < size; ++i) {
2176        x = - 10 + i%20;
2177        y = - 10 + (i/20);
2178        data[i] = exp(- (x * y)/(2 * sigma * sigma)) /
2179            (sigma * sqrt(2.0)) / mean * 2 + 1000;
2180        //data[i] = ((float)rand()) / RAND_MAX * 1.0;
2181    }
2182
2183    HeightMap* hmPtr = new HeightMap();
2184    float minx = 0.0f;
2185    float maxx = 1.0f;
2186    float miny = 0.5f;
2187    float maxy = 3.5f;
2188    hmPtr->setHeight(minx, miny, maxx, maxy, 20, 20, data);
2189    hmPtr->setColorMap(NanoVis::get_transfunc("default"));
2190    hmPtr->setVisible(true);
2191    hmPtr->setLineContourVisible(true);
2192    NanoVis::grid->setVisible(true);
2193    NanoVis::heightMap.push_back(hmPtr);
2194
2195    int image_width = 512;
2196    int image_height = 512;
2197
2198    NanoVis::render_2d_contour(hmPtr, image_width, image_height);
2199
2200    return TCL_OK;
2201}
2202
2203static int
2204HeightMapTransFuncOp(ClientData cdata, Tcl_Interp *interp, int objc,
2205                     Tcl_Obj *const *objv)
2206{
2207    const char *name;
2208    name = Tcl_GetString(objv[2]);
2209    TransferFunction *tf;
2210    tf = NanoVis::get_transfunc(name);
2211    if (tf == NULL) {
2212        Tcl_AppendResult(interp, "transfer function \"", name,
2213                         "\" is not defined", (char*)NULL);
2214        return TCL_ERROR;
2215    }
2216    vector<HeightMap *> imap;
2217    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
2218        return TCL_ERROR;
2219    }
2220    vector<HeightMap *>::iterator iter;
2221    for (iter = imap.begin(); iter != imap.end(); iter++) {
2222        (*iter)->setColorMap(tf);
2223    }
2224    return TCL_OK;
2225}
2226
2227static Rappture::CmdSpec heightMapOps[] = {
2228    {"create",       2, HeightMapCreateOp,      9, 9,
2229     "xmin ymin xmax ymax xnum ynum values",},
2230    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
2231    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
2232    {"legend",       2, HeightMapLegendOp,      5, 5, "index width height",},
2233    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
2234    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
2235    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
2236    {"test",         2, HeightMapTestOp,        2, 2, "",},
2237    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?indices?",},
2238
2239    // HELP ME
2240    // GOERGE
2241    {"topview",      2, HeightMapTopView,     2, 2, "",},
2242};
2243static int nHeightMapOps = NumCmdSpecs(heightMapOps);
2244
2245static int
2246HeightMapCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2247{
2248    Tcl_ObjCmdProc *proc;
2249
2250    proc = Rappture::GetOpFromObj(interp, nHeightMapOps, heightMapOps,
2251                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2252    if (proc == NULL) {
2253        return TCL_ERROR;
2254    }
2255    return (*proc) (cdata, interp, objc, objv);
2256}
2257
2258static int
2259GridAxisColorOp(ClientData cdata, Tcl_Interp *interp, int objc,
2260                Tcl_Obj *const *objv)
2261{
2262    float r, g, b, a;
2263    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2264        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2265        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2266        return TCL_ERROR;
2267    }
2268    a = 1.0f;
2269    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2270        return TCL_ERROR;
2271    }
2272    if (NanoVis::grid) {
2273        NanoVis::grid->setAxisColor(r, g, b, a);
2274    }
2275    return TCL_OK;
2276}
2277
2278static int
2279GridAxisNameOp(ClientData cdata, Tcl_Interp *interp, int objc,
2280               Tcl_Obj *const *objv)
2281{
2282    int axis;
2283    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
2284        return TCL_ERROR;
2285    }
2286    if (NanoVis::grid != NULL) {
2287        Axis *axisPtr;
2288
2289        axisPtr = NULL;     /* Suppress compiler warning. */
2290        switch (axis) {
2291        case 0: axisPtr = &NanoVis::grid->xAxis; break;
2292        case 1: axisPtr = &NanoVis::grid->yAxis; break;
2293        case 2: axisPtr = &NanoVis::grid->zAxis; break;
2294        }
2295        axisPtr->name(Tcl_GetString(objv[3]));
2296        axisPtr->units(Tcl_GetString(objv[4]));
2297    }
2298    return TCL_OK;
2299}
2300
2301static int
2302GridLineColorOp(ClientData cdata, Tcl_Interp *interp, int objc,
2303                Tcl_Obj *const *objv)
2304{
2305    float r, g, b, a;
2306    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2307        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2308        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2309        return TCL_ERROR;
2310    }
2311    a = 1.0f;
2312    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2313        return TCL_ERROR;
2314    }
2315    if (NanoVis::grid) {
2316        NanoVis::grid->setLineColor(r, g, b, a);
2317    }
2318    return TCL_OK;
2319}
2320
2321static int
2322GridVisibleOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2323{
2324    bool visible;
2325    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2326        return TCL_ERROR;
2327    }
2328    NanoVis::grid->setVisible(visible);
2329    return TCL_OK;
2330}
2331
2332static Rappture::CmdSpec gridOps[] = {
2333    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2334    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2335    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2336    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2337};
2338static int nGridOps = NumCmdSpecs(gridOps);
2339
2340static int
2341GridCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2342{
2343    Tcl_ObjCmdProc *proc;
2344
2345    proc = Rappture::GetOpFromObj(interp, nGridOps, gridOps,
2346        Rappture::CMDSPEC_ARG1, objc, objv, 0);
2347    if (proc == NULL) {
2348        return TCL_ERROR;
2349    }
2350    return (*proc) (cdata, interp, objc, objv);
2351}
2352
2353static int
2354AxisCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2355{
2356    if (objc < 2) {
2357        Tcl_AppendResult(interp, "wrong # args: should be \"",
2358                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2359        return TCL_ERROR;
2360    }
2361    const char *string = Tcl_GetString(objv[1]);
2362    char c = string[0];
2363    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2364        bool visible;
2365
2366        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2367            return TCL_ERROR;
2368        }
2369        NanoVis::axis_on = visible;
2370    } else {
2371        Tcl_AppendResult(interp, "bad axis option \"", string,
2372                         "\": should be visible", (char*)NULL);
2373        return TCL_ERROR;
2374    }
2375    return TCL_OK;
2376}
2377
2378#if PLANE_CMD
2379static int
2380PlaneNewOp(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2381{
2382    fprintf(stderr, "load plane for 2D visualization command\n");
2383    int index, w, h;
2384    if (objc != 4) {
2385        Tcl_AppendResult(interp, "wrong # args: should be \"",
2386            Tcl_GetString(objv[0]), " plane_index w h \"", (char*)NULL);
2387        return TCL_ERROR;
2388    }
2389    if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) {
2390        return TCL_ERROR;
2391    }
2392    if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) {
2393        return TCL_ERROR;
2394    }
2395    if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) {
2396        return TCL_ERROR;
2397    }
2398
2399    //Now read w*h*4 bytes. The server expects the plane to be a stream of
2400    //floats
2401    char* tmp = new char[int(w*h*sizeof(float))];
2402    if (tmp == NULL) {
2403        Tcl_AppendResult(interp, "can't allocate stream data", (char *)NULL);
2404        return TCL_ERROR;
2405    }
2406    bzero(tmp, w*h*4);
2407    int status = read(0, tmp, w*h*sizeof(float));
2408    if (status <= 0) {
2409        exit(0);                // Bail out on read error?  Should log the
2410                                // error and return a non-zero exit status.
2411    }
2412    plane[index] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, (float*)tmp);
2413    delete[] tmp;
2414    return TCL_OK;
2415}
2416
2417
2418static int
2419PlaneLinkOp(ClientData cdata, Tcl_Interp *interp, int objc,
2420            Tcl_Obj *const *objv)
2421{
2422    fprintf(stderr, "link the plane to the 2D renderer command\n");
2423
2424    int plane_index, tf_index;
2425
2426    if (objc != 3) {
2427        Tcl_AppendResult(interp, "wrong # args: should be \"",
2428            Tcl_GetString(objv[0]), " plane_index tf_index \"", (char*)NULL);
2429        return TCL_ERROR;
2430    }
2431    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2432        return TCL_ERROR;
2433    }
2434    if (Tcl_GetIntFromObj(interp, objv[2], &tf_index) != TCL_OK) {
2435        return TCL_ERROR;
2436    }
2437    //plane_render->add_plane(plane[plane_index], tf[tf_index]);
2438    return TCL_OK;
2439}
2440
2441//Enable a 2D plane for render
2442//The plane_index is the index mantained in the 2D plane renderer
2443static int
2444PlaneEnableOp(ClientData cdata, Tcl_Interp *interp, int objc,
2445              Tcl_Obj *const *objv)
2446{
2447    fprintf(stderr,"enable a plane so the 2D renderer can render it command\n");
2448
2449    if (objc != 3) {
2450        Tcl_AppendResult(interp, "wrong # args: should be \"",
2451            Tcl_GetString(objv[0]), " plane_index mode \"", (char*)NULL);
2452        return TCL_ERROR;
2453    }
2454    int plane_index;
2455    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2456        return TCL_ERROR;
2457    }
2458    int mode;
2459    if (Tcl_GetIntFromObj(interp, objv[2], &mode) != TCL_OK) {
2460        return TCL_ERROR;
2461    }
2462    if (mode == 0) {
2463        plane_index = -1;
2464    }
2465    plane_render->set_active_plane(plane_index);
2466    return TCL_OK;
2467}
2468
2469static Rappture::CmdSpec planeOps[] = {
2470    {"enable",     1, PlaneEnableOp,    4, 4, "planeIdx mode",},
2471    {"link",       1, PlaneLinkOp,      4, 4, "planeIdx transfuncIdx",},
2472    {"new",        1, PlaneNewOp,       5, 5, "planeIdx width height",},
2473};
2474static int nPlaneOps = NumCmdSpecs(planeOps);
2475
2476static int
2477PlaneCmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
2478{
2479    Tcl_ObjCmdProc *proc;
2480
2481    proc = Rappture::GetOpFromObj(interp, nPlaneOps, planeOps,
2482                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2483    if (proc == NULL) {
2484        return TCL_ERROR;
2485    }
2486    return (*proc) (cdata, interp, objc, objv);
2487}
2488
2489#endif  /*PLANE_CMD*/
2490
2491/*
2492 * This command should be Tcl procedure instead of a C command.  The reason
2493 * for this that 1) we are using a safe interpreter so we would need a master
2494 * interpreter to load the Tcl environment properly (including our "unirect2d"
2495 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2496 * easy to add new directories for procedures, since it's loaded into /tmp.
2497 *
2498 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2499 * to verify the structure and then pass it to the appropiate Tcl command
2500 * (heightmap, volume, etc). Our C command always creates a heightmap.
2501 */
2502static int
2503UniRect2dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2504             Tcl_Obj *const *objv)
2505{
2506    int xNum, yNum, zNum;
2507    float xMin, yMin, xMax, yMax;
2508    float *zValues;
2509    const char *xUnits, *yUnits, *zUnits;
2510
2511    if ((objc & 0x01) == 0) {
2512        Tcl_AppendResult(interp, Tcl_GetString(objv[0]), ": ",
2513                "wrong number of arguments: should be key-value pairs",
2514                (char *)NULL);
2515        return TCL_ERROR;
2516    }
2517    zValues = NULL;
2518    xNum = yNum = zNum = 0;
2519    xMin = yMin = xMax = yMax = 0.0f;
2520    xUnits = yUnits = zUnits = NULL;
2521    int i;
2522    for (i = 1; i < objc; i += 2) {
2523        const char *string;
2524        char c;
2525
2526        string = Tcl_GetString(objv[i]);
2527        c = string[0];
2528        if ((c == 'x') && (strcmp(string, "xmin") == 0)) {
2529            if (GetFloatFromObj(interp, objv[i+1], &xMin) != TCL_OK) {
2530                return TCL_ERROR;
2531            }
2532        } else if ((c == 'x') && (strcmp(string, "xmax") == 0)) {
2533            if (GetFloatFromObj(interp, objv[i+1], &xMax) != TCL_OK) {
2534                return TCL_ERROR;
2535            }
2536        } else if ((c == 'x') && (strcmp(string, "xnum") == 0)) {
2537            if (Tcl_GetIntFromObj(interp, objv[i+1], &xNum) != TCL_OK) {
2538                return TCL_ERROR;
2539            }
2540            if (xNum <= 0) {
2541                Tcl_AppendResult(interp, "bad xnum value: must be > 0",
2542                                 (char *)NULL);
2543                return TCL_ERROR;
2544            }
2545        } else if ((c == 'x') && (strcmp(string, "xunits") == 0)) {
2546            xUnits = Tcl_GetString(objv[i+1]);
2547        } else if ((c == 'y') && (strcmp(string, "ymin") == 0)) {
2548            if (GetFloatFromObj(interp, objv[i+1], &yMin) != TCL_OK) {
2549                return TCL_ERROR;
2550            }
2551        } else if ((c == 'y') && (strcmp(string, "ymax") == 0)) {
2552            if (GetFloatFromObj(interp, objv[i+1], &yMax) != TCL_OK) {
2553                return TCL_ERROR;
2554            }
2555        } else if ((c == 'y') && (strcmp(string, "ynum") == 0)) {
2556            if (Tcl_GetIntFromObj(interp, objv[i+1], &yNum) != TCL_OK) {
2557                return TCL_ERROR;
2558            }
2559            if (yNum <= 0) {
2560                Tcl_AppendResult(interp, "bad ynum value: must be > 0",
2561                                 (char *)NULL);
2562                return TCL_ERROR;
2563            }
2564        } else if ((c == 'y') && (strcmp(string, "yunits") == 0)) {
2565            yUnits = Tcl_GetString(objv[i+1]);
2566        } else if ((c == 'z') && (strcmp(string, "zvalues") == 0)) {
2567            Tcl_Obj **zObj;
2568
2569            if (Tcl_ListObjGetElements(interp, objv[i+1], &zNum, &zObj)!= TCL_OK) {
2570                return TCL_ERROR;
2571            }
2572            int j;
2573            zValues = new float[zNum];
2574            for (j = 0; j < zNum; j++) {
2575                if (GetFloatFromObj(interp, zObj[j], zValues + j) != TCL_OK) {
2576                    return TCL_ERROR;
2577                }
2578            }
2579        } else if ((c == 'z') && (strcmp(string, "zunits") == 0)) {
2580            zUnits = Tcl_GetString(objv[i+1]);
2581        } else {
2582            Tcl_AppendResult(interp, "unknown key \"", string,
2583                "\": should be xmin, xmax, xnum, xunits, ymin, ymax, ynum, yunits, zvalues, or zunits",
2584                (char *)NULL);
2585            return TCL_ERROR;
2586        }
2587    }
2588    if (zValues == NULL) {
2589        Tcl_AppendResult(interp, "missing \"zvalues\" key", (char *)NULL);
2590        return TCL_ERROR;
2591    }
2592    if (zNum != (xNum * yNum)) {
2593        Tcl_AppendResult(interp, "wrong number of z values must be xnum*ynum",
2594                (char *)NULL);
2595        return TCL_ERROR;
2596    }
2597    HeightMap* hmPtr;
2598    hmPtr = new HeightMap();
2599
2600    // Must set units before the heights.
2601    hmPtr->xAxis.units(xUnits);
2602    hmPtr->yAxis.units(yUnits);
2603    hmPtr->zAxis.units(zUnits);
2604    hmPtr->wAxis.units(yUnits);
2605    hmPtr->setHeight(xMin, yMin, xMax, yMax, xNum, yNum, zValues);
2606    hmPtr->setColorMap(NanoVis::get_transfunc("default"));
2607    hmPtr->setVisible(true);
2608    hmPtr->setLineContourVisible(true);
2609    NanoVis::heightMap.push_back(hmPtr);
2610    return TCL_OK;
2611}
2612
2613
2614Tcl_Interp *
2615initTcl()
2616{
2617
2618    /*
2619     * Ideally the connection is authenticated by nanoscale.  I still like the
2620     * idea of creating a non-safe master interpreter with a safe slave
2621     * interpreter.  Alias all the nanovis commands in the slave. That way we
2622     * can still run Tcl code within nanovis.  The eventual goal is to create
2623     * a test harness through the interpreter for nanovis.
2624     */
2625    Tcl_Interp *interp;
2626    interp = Tcl_CreateInterp();
2627    Tcl_MakeSafe(interp);
2628
2629    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
2630    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
2631    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
2632    Tcl_CreateObjCommand(interp, "flow",        FlowCmd,        NULL, NULL);
2633    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
2634    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
2635    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
2636    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
2637    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    NULL, NULL);
2638    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   NULL, NULL);
2639    Tcl_CreateObjCommand(interp, "unirect2d",   UniRect2dCmd,   NULL, NULL);
2640    Tcl_CreateObjCommand(interp, "up",          UpCmd,          NULL, NULL);
2641    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
2642#if __TEST_CODE__
2643    Tcl_CreateObjCommand(interp, "test", TestCmd, NULL, NULL);
2644#endif
2645
2646    // create a default transfer function
2647    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2648        fprintf(NanoVis::logfile, "WARNING: bad default transfer function\n");
2649        fprintf(NanoVis::logfile, Tcl_GetStringResult(interp));
2650    }
2651    return interp;
2652}
2653
2654
Note: See TracBrowser for help on using the repository browser.