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

Last change on this file since 2930 was 2930, checked in by ldelgass, 13 years ago

Rename offscreenBufferCapture to bindOffscreenBuffer. Fix more GL state leaks.
Require OpenGL 2.1 as a minimum. Style changes for PlaneRenderer? and rework
plane commands.

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