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

Last change on this file since 3608 was 3608, checked in by ldelgass, 12 years ago

Fix for flow video capture

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