source: nanovis/branches/1.1/Command.cpp @ 4874

Last change on this file since 4874 was 4874, checked in by ldelgass, 5 years ago

Add command line options to set I/O file descriptors, merge some refactoring to
prep for merging threading support.

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