source: nanovis/trunk/Command.cpp @ 5722

Last change on this file since 5722 was 5479, checked in by ldelgass, 4 years ago

whitespace

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