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

Last change on this file since 3884 was 3884, checked in by ldelgass, 6 years ago

Fix cutplane visible command

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