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

Last change on this file since 3377 was 3377, checked in by gah, 10 years ago

rework of stats log file.

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