source: nanovis/trunk/Command.cpp @ 4822

Last change on this file since 4822 was 4822, checked in by ldelgass, 9 years ago

Move md5 include

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