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

Last change on this file since 2831 was 2831, checked in by ldelgass, 9 years ago

Refactor texture classes, misc. cleanups, cut down on header pollution -- still
need to fix header deps in Makefile.in

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