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

Last change on this file since 1510 was 1510, checked in by gah, 15 years ago

Found memory leak in reset_patterns

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