source: nanovis/trunk/Command.cpp @ 4795

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

Don't add extra pid to stats log line

  • 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/**
348 * Used internally to decode a series of volume index values and
349 * store then in the specified vector.  If there are no volume index
350 * arguments, this means "all volumes" to most commands, so all
351 * active volume indices are stored in the vector.
352 *
353 * Updates pushes index values into the vector.  Returns TCL_OK or
354 * TCL_ERROR to indicate an error.
355 */
356int
357nv::GetVolumeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Volume **volPtrPtr)
358{
359    const char *string = Tcl_GetString(objPtr);
360
361    NanoVis::VolumeHashmap::iterator itr = NanoVis::volumeTable.find(string);
362    if (itr == NanoVis::volumeTable.end()) {
363        if (interp != NULL) {
364            Tcl_AppendResult(interp, "can't find a volume named \"",
365                         string, "\"", (char*)NULL);
366        }
367        return TCL_ERROR;
368    }
369    *volPtrPtr = itr->second;
370    return TCL_OK;
371}
372
373/**
374 * Used internally to decode a series of volume index values and
375 * store then in the specified vector.  If there are no volume index
376 * arguments, this means "all volumes" to most commands, so all
377 * active volume indices are stored in the vector.
378 *
379 * Updates pushes index values into the vector.  Returns TCL_OK or
380 * TCL_ERROR to indicate an error.
381 */
382static int
383GetVolumes(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
384           std::vector<Volume *>* vectorPtr)
385{
386    if (objc == 0) {
387        // No arguments. Get all volumes.
388        NanoVis::VolumeHashmap::iterator itr;
389        for (itr = NanoVis::volumeTable.begin();
390             itr != NanoVis::volumeTable.end(); ++itr) {
391            vectorPtr->push_back(itr->second);
392        }
393    } else {
394        // Get the volumes associated with the given index arguments.
395        for (int n = 0; n < objc; n++) {
396            Volume *volume;
397            if (GetVolumeFromObj(interp, objv[n], &volume) != TCL_OK) {
398                return TCL_ERROR;
399            }
400            vectorPtr->push_back(volume);
401        }
402    }
403    return TCL_OK;
404}
405
406/**
407 * Used internally to decode a series of volume index values and
408 * store then in the specified vector.  If there are no volume index
409 * arguments, this means "all volumes" to most commands, so all
410 * active volume indices are stored in the vector.
411 *
412 * Updates pushes index values into the vector.  Returns TCL_OK or
413 * TCL_ERROR to indicate an error.
414 */
415static int
416GetHeightMaps(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
417              std::vector<HeightMap *>* vectorPtr)
418{
419    if (objc == 0) {
420        // No arguments. Get all heightmaps.
421        NanoVis::HeightMapHashmap::iterator itr;
422        for (itr = NanoVis::heightMapTable.begin();
423             itr != NanoVis::heightMapTable.end(); ++itr) {
424            vectorPtr->push_back(itr->second);
425        }
426    } else {
427        for (int n = 0; n < objc; n++) {
428            HeightMap *heightMap;
429            if (GetHeightMapFromObj(interp, objv[n], &heightMap) != TCL_OK) {
430                return TCL_ERROR;
431            }
432            vectorPtr->push_back(heightMap);
433        }
434    }
435    return TCL_OK;
436}
437
438/**
439 * Used internally to decode an axis value from a string ("x", "y",
440 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
441 * along with a value in valPtr.  Otherwise, it returns TCL_ERROR
442 * and an error message in the interpreter.
443 */
444static int
445GetAxis(Tcl_Interp *interp, const char *string, int *indexPtr)
446{
447    if (string[1] == '\0') {
448        char c;
449
450        c = tolower((unsigned char)string[0]);
451        if (c == 'x') {
452            *indexPtr = 0;
453            return TCL_OK;
454        } else if (c == 'y') {
455            *indexPtr = 1;
456            return TCL_OK;
457        } else if (c == 'z') {
458            *indexPtr = 2;
459            return TCL_OK;
460        }
461        /*FALLTHRU*/
462    }
463    Tcl_AppendResult(interp, "bad axis \"", string,
464                     "\": should be x, y, or z", (char*)NULL);
465    return TCL_ERROR;
466}
467
468/**
469 * Used internally to decode an axis value from a string ("x", "y",
470 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
471 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
472 * and an error message in the interpreter.
473 */
474int
475nv::GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr)
476{
477    return GetAxis(interp, Tcl_GetString(objPtr), indexPtr);
478}
479
480/**
481 * Used internally to decode an axis value from a string ("x", "y",
482 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
483 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
484 * and an error message in the interpreter.
485 */
486static int
487GetAxisDirFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr, int *dirPtr)
488{
489    const char *string = Tcl_GetString(objPtr);
490
491    int sign = 1;
492    if (*string == '-') {
493        sign = -1;
494        string++;
495    }
496    if (GetAxis(interp, string, indexPtr) != TCL_OK) {
497        return TCL_ERROR;
498    }
499    if (dirPtr != NULL) {
500        *dirPtr = sign;
501    }
502    return TCL_OK;
503}
504
505/**
506 * Used internally to decode a color value from a string ("R G B")
507 * as a list of three numbers 0-1.  Returns TCL_OK if successful,
508 * along with RGB values in rgbPtr.  Otherwise, it returns TCL_ERROR
509 * and an error message in the interpreter.
510 */
511static int
512GetColor(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, float *rgbPtr)
513{
514    if (objc < 3) {
515        Tcl_AppendResult(interp, "missing color values\": ",
516                         "should list of R G B values 0.0 - 1.0", (char*)NULL);
517        return TCL_ERROR;
518    }
519    if ((GetFloatFromObj(interp, objv[0], rgbPtr + 0) != TCL_OK) ||
520        (GetFloatFromObj(interp, objv[1], rgbPtr + 1) != TCL_OK) ||
521        (GetFloatFromObj(interp, objv[2], rgbPtr + 2) != TCL_OK)) {
522        return TCL_ERROR;
523    }
524    return TCL_OK;
525}
526
527/**
528 * Read the requested number of bytes from standard input into the given
529 * buffer.  The buffer must have already been allocated for nBytes.  The
530 * buffer is then decompressed and decoded.
531 */
532int
533nv::GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes)
534{
535    if (!SocketRead((char *)buf.bytes(), nBytes)) {
536        return TCL_ERROR;
537    }
538    buf.count(nBytes);
539    Rappture::Outcome err;
540    TRACE("Checking header[%.13s]", buf.bytes());
541    if (strncmp (buf.bytes(), "@@RP-ENC:", 9) == 0) {
542        /* There's a header on the buffer, use it to decode the data. */
543        if (!Rappture::encoding::decode(err, buf, RPENC_HDR)) {
544            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
545            return TCL_ERROR;
546        }
547    } else if (Rappture::encoding::isBase64(buf.bytes(), buf.size())) {
548        /* No header, but it's base64 encoded.  It's likely that it's both
549         * base64 encoded and compressed. */
550        if (!Rappture::encoding::decode(err, buf, RPENC_B64 | RPENC_Z)) {
551            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
552            return TCL_ERROR;
553        }
554    }
555    return TCL_OK;
556}
557
558static int
559CameraAngleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
560              Tcl_Obj *const *objv)
561{
562    float theta, phi, psi;
563    if ((GetFloatFromObj(interp, objv[2], &phi) != TCL_OK) ||
564        (GetFloatFromObj(interp, objv[3], &theta) != TCL_OK) ||
565        (GetFloatFromObj(interp, objv[4], &psi) != TCL_OK)) {
566        return TCL_ERROR;
567    }
568    //NanoVis::orientCamera(phi, theta, psi);
569    //return TCL_OK;
570    Tcl_AppendResult(interp, "The 'camera angle' command is deprecated, use 'camera orient'", (char*)NULL);
571    return TCL_ERROR;
572}
573
574static int
575CameraOrientOp(ClientData clientData, Tcl_Interp *interp, int objc, 
576               Tcl_Obj *const *objv)
577{
578    double quat[4];
579    if ((Tcl_GetDoubleFromObj(interp, objv[2], &quat[3]) != TCL_OK) ||
580        (Tcl_GetDoubleFromObj(interp, objv[3], &quat[0]) != TCL_OK) ||
581        (Tcl_GetDoubleFromObj(interp, objv[4], &quat[1]) != TCL_OK) ||
582        (Tcl_GetDoubleFromObj(interp, objv[5], &quat[2]) != TCL_OK)) {
583        return TCL_ERROR;
584    }
585    NanoVis::orientCamera(quat);
586    return TCL_OK;
587}
588
589static int
590CameraPanOp(ClientData clientData, Tcl_Interp *interp, int objc, 
591             Tcl_Obj *const *objv)
592{
593    float x, y;
594    if ((GetFloatFromObj(interp, objv[2], &x) != TCL_OK) ||
595        (GetFloatFromObj(interp, objv[3], &y) != TCL_OK)) {
596        return TCL_ERROR;
597    }
598    NanoVis::panCamera(x, y);
599    return TCL_OK;
600}
601
602static int
603CameraPositionOp(ClientData clientData, Tcl_Interp *interp, int objc, 
604                 Tcl_Obj *const *objv)
605{
606    Vector3f pos;
607    if ((GetFloatFromObj(interp, objv[2], &pos.x) != TCL_OK) ||
608        (GetFloatFromObj(interp, objv[3], &pos.y) != TCL_OK) ||
609        (GetFloatFromObj(interp, objv[4], &pos.z) != TCL_OK)) {
610        return TCL_ERROR;
611    }
612
613    NanoVis::setCameraPosition(pos);
614    return TCL_OK;
615}
616
617static int
618CameraResetOp(ClientData clientData, Tcl_Interp *interp, int objc, 
619              Tcl_Obj *const *objv)
620{
621    bool all = false;
622    if (objc == 3) {
623        const char *string = Tcl_GetString(objv[2]);
624        char c = string[0];
625        if ((c != 'a') || (strcmp(string, "all") != 0)) {
626            Tcl_AppendResult(interp, "bad camera reset option \"", string,
627                             "\": should be all", (char*)NULL);
628            return TCL_ERROR;
629        }
630        all = true;
631    }
632    NanoVis::resetCamera(all);
633    return TCL_OK;
634}
635
636static int
637CameraZoomOp(ClientData clientData, Tcl_Interp *interp, int objc, 
638             Tcl_Obj *const *objv)
639{
640    float z;
641    if (GetFloatFromObj(interp, objv[2], &z) != TCL_OK) {
642        return TCL_ERROR;
643    }
644    NanoVis::zoomCamera(z);
645    return TCL_OK;
646}
647
648static CmdSpec cameraOps[] = {
649    {"angle",   2, CameraAngleOp,    5, 5, "xAngle yAngle zAngle",},
650    {"orient",  1, CameraOrientOp,   6, 6, "qw qx qy qz",},
651    {"pan",     2, CameraPanOp,      4, 4, "x y",},
652    {"pos",     2, CameraPositionOp, 5, 5, "x y z",},
653    {"reset",   1, CameraResetOp,    2, 3, "?all?",},
654    {"zoom",    1, CameraZoomOp,     3, 3, "factor",},
655};
656static int nCameraOps = NumCmdSpecs(cameraOps);
657
658static int
659CameraCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
660          Tcl_Obj *const *objv)
661{
662    Tcl_ObjCmdProc *proc;
663
664    proc = GetOpFromObj(interp, nCameraOps, cameraOps,
665                        CMDSPEC_ARG1, objc, objv, 0);
666    if (proc == NULL) {
667        return TCL_ERROR;
668    }
669    return (*proc) (clientData, interp, objc, objv);
670}
671
672static int
673SnapshotCmd(ClientData clientData, Tcl_Interp *interp, int objc,
674            Tcl_Obj *const *objv)
675{
676    int origWidth, origHeight, width, height;
677
678    origWidth = NanoVis::winWidth;
679    origHeight = NanoVis::winHeight;
680    width = 2048;
681    height = 2048;
682
683    NanoVis::resizeOffscreenBuffer(width, height);
684    NanoVis::bindOffscreenBuffer();
685    NanoVis::render();
686    NanoVis::readScreen();
687#ifdef USE_THREADS
688    queuePPM(g_queue, "nv>image -type print -bytes",
689             NanoVis::screenBuffer, width, height);
690#else
691    writePPM(g_fdOut, "nv>image -type print -bytes",
692             NanoVis::screenBuffer, width, height);
693#endif
694    NanoVis::resizeOffscreenBuffer(origWidth, origHeight);
695
696    return TCL_OK;
697}
698
699static int
700CutplanePositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
701                   Tcl_Obj *const *objv)
702{
703    float relval;
704    if (GetFloatFromObj(interp, objv[2], &relval) != TCL_OK) {
705        return TCL_ERROR;
706    }
707
708    // keep this just inside the volume so it doesn't disappear
709    if (relval < 0.01f) {
710        relval = 0.01f;
711    } else if (relval > 0.99f) {
712        relval = 0.99f;
713    }
714
715    int axis;
716    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
717        return TCL_ERROR;
718    }
719
720    std::vector<Volume *> ivol;
721    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
722        return TCL_ERROR;
723    }
724    std::vector<Volume *>::iterator iter;
725    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
726        (*iter)->setCutplanePosition(axis, relval);
727    }
728    return TCL_OK;
729}
730
731static int
732CutplaneStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
733                Tcl_Obj *const *objv)
734{
735    bool state;
736    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
737        return TCL_ERROR;
738    }
739
740    int axis;
741    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
742        return TCL_ERROR;
743    }
744
745    std::vector<Volume *> ivol;
746    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
747        return TCL_ERROR;
748    }
749    if (state) {
750        std::vector<Volume *>::iterator iter;
751        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
752            (*iter)->enableCutplane(axis);
753        }
754    } else {
755        std::vector<Volume *>::iterator iter;
756        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
757            (*iter)->disableCutplane(axis);
758        }
759    }
760    return TCL_OK;
761}
762
763static int
764CutplaneVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
765                  Tcl_Obj *const *objv)
766{
767    bool state;
768    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
769        return TCL_ERROR;
770    }
771
772    std::vector<Volume *> ivol;
773    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
774        return TCL_ERROR;
775    }
776    std::vector<Volume *>::iterator iter;
777    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
778        (*iter)->cutplanesVisible(state);
779    }
780    return TCL_OK;
781}
782
783static CmdSpec cutplaneOps[] = {
784    {"position", 1, CutplanePositionOp, 4, 0, "relval axis ?indices?",},
785    {"state",    1, CutplaneStateOp,    4, 0, "bool axis ?indices?",},
786    {"visible",  1, CutplaneVisibleOp,  3, 0, "bool ?indices?",},
787};
788static int nCutplaneOps = NumCmdSpecs(cutplaneOps);
789
790/*
791 * ----------------------------------------------------------------------
792 * CLIENT COMMAND:
793 *   cutplane state on|off <axis> ?<volume>...?
794 *   cutplane position <relvalue> <axis> ?<volume>...?
795 *
796 * Clients send these commands to manipulate the cutplanes in one or
797 * more data volumes.  The "state" command turns a cutplane on or
798 * off.  The "position" command changes the position to a relative
799 * value in the range 0-1.  The <axis> can be x, y, or z.  These
800 * options are applied to the volumes represented by one or more
801 * <volume> indices.  If no volumes are specified, then all volumes
802 * are updated.
803 * ----------------------------------------------------------------------
804 */
805static int
806CutplaneCmd(ClientData clientData, Tcl_Interp *interp, int objc,
807            Tcl_Obj *const *objv)
808{
809    Tcl_ObjCmdProc *proc;
810
811    proc = GetOpFromObj(interp, nCutplaneOps, cutplaneOps,
812                        CMDSPEC_ARG1, objc, objv, 0);
813    if (proc == NULL) {
814        return TCL_ERROR;
815    }
816    return (*proc) (clientData, interp, objc, objv);
817}
818
819/*
820 * ClientInfoCmd --
821 *
822 *      Log initial values to stats file.  The first time this is called
823 *      "render_start" is written into the stats file.  Afterwards, it
824 *      is "render_info".
825 *       
826 *         clientinfo list
827 */
828static int
829ClientInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
830              Tcl_Obj *const *objv)
831{
832    Tcl_DString ds;
833    Tcl_Obj *objPtr, *listObjPtr, **items;
834    int result;
835    int i, numItems, length;
836    char buf[BUFSIZ];
837    const char *string;
838    static int first = 1;
839
840    if (objc != 2) {
841        Tcl_AppendResult(interp, "wrong # of arguments: should be \"", 
842                Tcl_GetString(objv[0]), " list\"", (char *)NULL);
843        return TCL_ERROR;
844    }
845#ifdef KEEPSTATS
846    /* Use the initial client key value pairs as the parts for a generating
847     * a unique file name. */
848    int f = getStatsFile(objv[1]);
849    if (f < 0) {
850        Tcl_AppendResult(interp, "can't open stats file: ", 
851                         Tcl_PosixError(interp), (char *)NULL);
852        return TCL_ERROR;
853    }
854#endif
855    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
856    Tcl_IncrRefCount(listObjPtr);
857    if (first) {
858        first = false;
859        objPtr = Tcl_NewStringObj("render_start", 12);
860        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
861        /* renderer */
862        objPtr = Tcl_NewStringObj("renderer", 8);
863        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
864        objPtr = Tcl_NewStringObj("nanovis", 7);
865        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
866        /* pid */
867        objPtr = Tcl_NewStringObj("pid", 3);
868        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
869        Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj((long)g_stats.pid));
870        /* host */
871        objPtr = Tcl_NewStringObj("host", 4);
872        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
873        gethostname(buf, BUFSIZ-1);
874        buf[BUFSIZ-1] = '\0';
875        objPtr = Tcl_NewStringObj(buf, -1);
876        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
877    } else {
878        objPtr = Tcl_NewStringObj("render_info", 11);
879        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
880    }
881    Tcl_DStringInit(&ds);
882    /* date */
883    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4));
884    strcpy(buf, ctime(&g_stats.start.tv_sec));
885    buf[strlen(buf) - 1] = '\0';
886    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1));
887    /* date_secs */
888    Tcl_ListObjAppendElement(interp, listObjPtr, 
889                Tcl_NewStringObj("date_secs", 9));
890    Tcl_ListObjAppendElement(interp, listObjPtr, 
891                Tcl_NewLongObj(g_stats.start.tv_sec));
892    /* Client arguments. */
893    if (Tcl_ListObjGetElements(interp, objv[1], &numItems, &items) != TCL_OK) {
894        return TCL_ERROR;
895    }
896    for (i = 0; i < numItems; i++) {
897        Tcl_ListObjAppendElement(interp, listObjPtr, items[i]);
898    }
899    Tcl_DStringInit(&ds);
900    string = Tcl_GetStringFromObj(listObjPtr, &length);
901    Tcl_DStringAppend(&ds, string, length);
902    Tcl_DStringAppend(&ds, "\n", 1);
903#ifdef KEEPSTATS
904    result = writeToStatsFile(f, Tcl_DStringValue(&ds), 
905                              Tcl_DStringLength(&ds));
906#else
907    TRACE("clientinfo: %s", Tcl_DStringValue(&ds));
908#endif
909    Tcl_DStringFree(&ds);
910    Tcl_DecrRefCount(listObjPtr);
911    return result;
912}
913
914/*
915 * ----------------------------------------------------------------------
916 * CLIENT COMMAND:
917 *   legend <volumeIndex> <width> <height>
918 *
919 * Clients use this to generate a legend image for the specified
920 * transfer function.  The legend image is a color gradient from 0
921 * to one, drawn in the given transfer function.  The resulting image
922 * is returned in the size <width> x <height>.
923 * ----------------------------------------------------------------------
924 */
925static int
926LegendCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
927          Tcl_Obj *const *objv)
928{
929    if (objc != 4) {
930        Tcl_AppendResult(interp, "wrong # args: should be \"",
931            Tcl_GetString(objv[0]), " transfunc width height\"", (char*)NULL);
932        return TCL_ERROR;
933    }
934
935    const char *tfName = Tcl_GetString(objv[1]);
936    TransferFunction *tf = NanoVis::getTransferFunction(tfName);
937    if (tf == NULL) {
938        Tcl_AppendResult(interp, "unknown transfer function \"", tfName, "\"",
939                             (char*)NULL);
940        return TCL_ERROR;
941    }
942    int w, h;
943    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
944        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
945        return TCL_ERROR;
946    }
947    if (Volume::updatePending) {
948        NanoVis::setVolumeRanges();
949    }
950    NanoVis::renderLegend(tf, Volume::valueMin, Volume::valueMax, w, h, tfName);
951    return TCL_OK;
952}
953
954static int
955ScreenBgColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
956                Tcl_Obj *const *objv)
957{
958    float rgb[3];
959    if ((GetFloatFromObj(interp, objv[2], &rgb[0]) != TCL_OK) ||
960        (GetFloatFromObj(interp, objv[3], &rgb[1]) != TCL_OK) ||
961        (GetFloatFromObj(interp, objv[4], &rgb[2]) != TCL_OK)) {
962        return TCL_ERROR;
963    }
964    NanoVis::setBgColor(rgb);
965    return TCL_OK;
966}
967
968/*
969 * ----------------------------------------------------------------------
970 * CLIENT COMMAND:
971 *   screen size <width> <height>
972 *
973 * Clients send this command to set the size of the rendering area.
974 * Future images are generated at the specified width/height.
975 * ----------------------------------------------------------------------
976 */
977static int
978ScreenSizeOp(ClientData clientData, Tcl_Interp *interp, int objc, 
979             Tcl_Obj *const *objv)
980{
981    int w, h;
982    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
983        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
984        return TCL_ERROR;
985    }
986    NanoVis::resizeOffscreenBuffer(w, h);
987    return TCL_OK;
988}
989
990static CmdSpec screenOps[] = {
991    {"bgcolor",  1, ScreenBgColorOp,  5, 5, "r g b",},
992    {"size",     1, ScreenSizeOp, 4, 4, "width height",},
993};
994static int nScreenOps = NumCmdSpecs(screenOps);
995
996static int
997ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
998          Tcl_Obj *const *objv)
999{
1000    Tcl_ObjCmdProc *proc;
1001
1002    proc = GetOpFromObj(interp, nScreenOps, screenOps,
1003                        CMDSPEC_ARG1, objc, objv, 0);
1004    if (proc == NULL) {
1005        return TCL_ERROR;
1006    }
1007    return (*proc) (clientData, interp, objc, objv);
1008}
1009
1010/*
1011 * ----------------------------------------------------------------------
1012 * CLIENT COMMAND:
1013 *   transfunc define <name> <colormap> <alphamap>
1014 *     where <colormap> = { <v> <r> <g> <b> ... }
1015 *           <alphamap> = { <v> <w> ... }
1016 *
1017 * Clients send these commands to manipulate the transfer functions.
1018 * ----------------------------------------------------------------------
1019 */
1020static int
1021TransfuncCmd(ClientData clientData, Tcl_Interp *interp, int objc,
1022             Tcl_Obj *const *objv)
1023{
1024    if (objc < 2) {
1025        Tcl_AppendResult(interp, "wrong # args: should be \"",
1026                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
1027        return TCL_ERROR;
1028    }
1029
1030    const char *string = Tcl_GetString(objv[1]);
1031    char c = string[0];
1032    if ((c == 'd') && (strcmp(string, "define") == 0)) {
1033        if (objc != 5) {
1034            Tcl_AppendResult(interp, "wrong # args: should be \"",
1035                Tcl_GetString(objv[0]), " define name colorMap alphaMap\"",
1036                (char*)NULL);
1037            return TCL_ERROR;
1038        }
1039
1040        // decode the data and store in a series of fields
1041        int cmapc, amapc, i;
1042        Tcl_Obj **cmapv;
1043        Tcl_Obj **amapv;
1044
1045        amapv = cmapv = NULL;
1046        if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
1047            return TCL_ERROR;
1048        }
1049        if ((cmapc % 4) != 0) {
1050            Tcl_AppendResult(interp, "wrong # elements is colormap: should be ",
1051                "{ v r g b ... }", (char*)NULL);
1052            return TCL_ERROR;
1053        }
1054        if (Tcl_ListObjGetElements(interp, objv[4], &amapc, &amapv) != TCL_OK) {
1055            return TCL_ERROR;
1056        }
1057        if ((amapc % 2) != 0) {
1058            Tcl_AppendResult(interp, "wrong # elements in alphamap: should be ",
1059                " { v w ... }", (char*)NULL);
1060            return TCL_ERROR;
1061        }
1062
1063        int numColors = cmapc/4;
1064        float *colorKeys = new float[numColors];
1065        Vector3f *colors = new Vector3f[numColors];
1066        for (i = 0; i < cmapc; i += 4) {
1067            int j;
1068            double q[4];
1069
1070            for (j=0; j < 4; j++) {
1071                if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &q[j]) != TCL_OK) {
1072                    return TCL_ERROR;
1073                }
1074                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1075                    Tcl_AppendResult(interp, "bad colormap value \"",
1076                        Tcl_GetString(cmapv[i+j]),
1077                        "\": should be in the range 0-1", (char*)NULL);
1078                    return TCL_ERROR;
1079                }
1080            }
1081
1082            colorKeys[i/4] = (float)q[0];
1083            colors[i/4].set((float)q[1], (float)q[2], (float)q[3]);
1084        }
1085        int numAlphas = amapc/2;
1086        float *alphaKeys = new float[numAlphas];
1087        float *alphas = new float[numAlphas];
1088        for (i=0; i < amapc; i += 2) {
1089            double q[2];
1090            int j;
1091
1092            for (j=0; j < 2; j++) {
1093                if (Tcl_GetDoubleFromObj(interp, amapv[i+j], &q[j]) != TCL_OK) {
1094                    return TCL_ERROR;
1095                }
1096                if ((q[j] < 0.0) || (q[j] > 1.0)) {
1097                    Tcl_AppendResult(interp, "bad alphamap value \"",
1098                        Tcl_GetString(amapv[i+j]),
1099                        "\": should be in the range 0-1", (char*)NULL);
1100                    return TCL_ERROR;
1101                }
1102            }
1103
1104            alphaKeys[i/2] = (float)q[0];
1105            alphas[i/2] = (float)q[1];
1106        }
1107        // sample the given function into discrete slots
1108        const int nslots = 256;
1109        float data[4*nslots];
1110        for (i=0; i < nslots; i++) {
1111            float x = float(i)/(nslots-1);
1112            Vector3f color;
1113            float alpha;
1114            TransferFunction::sample(x, colorKeys, colors, numColors, &color);
1115            TransferFunction::sample(x, alphaKeys, alphas, numAlphas, &alpha);
1116
1117            data[4*i]   = color.r;
1118            data[4*i+1] = color.g;
1119            data[4*i+2] = color.b;
1120            data[4*i+3] = alpha;
1121        }
1122        delete [] colorKeys;
1123        delete [] colors;
1124        delete [] alphaKeys;
1125        delete [] alphas;
1126        // find or create this transfer function
1127        NanoVis::defineTransferFunction(Tcl_GetString(objv[2]), nslots, data);
1128    } else {
1129        Tcl_AppendResult(interp, "bad option \"", string,
1130                "\": should be define", (char*)NULL);
1131        return TCL_ERROR;
1132    }
1133    return TCL_OK;
1134}
1135
1136/*
1137 * ----------------------------------------------------------------------
1138 * CLIENT COMMAND:
1139 *   up axis
1140 *
1141 * Clients use this to set the "up" direction for all volumes.  Volumes
1142 * are oriented such that this direction points upward.
1143 * ----------------------------------------------------------------------
1144 */
1145static int
1146UpCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
1147{
1148    if (objc != 2) {
1149        Tcl_AppendResult(interp, "wrong # args: should be \"",
1150                         Tcl_GetString(objv[0]), " x|y|z|-x|-y|-z\"", (char*)NULL);
1151        return TCL_ERROR;
1152    }
1153
1154    int sign;
1155    int axis;
1156    if (GetAxisDirFromObj(interp, objv[1], &axis, &sign) != TCL_OK) {
1157        return TCL_ERROR;
1158    }
1159    NanoVis::setCameraUpdir(Camera::AxisDirection((axis+1)*sign));
1160    return TCL_OK;
1161}
1162
1163static int
1164VolumeAnimationCaptureOp(ClientData clientData, Tcl_Interp *interp, int objc,
1165                         Tcl_Obj *const *objv)
1166{
1167    int total;
1168    if (Tcl_GetIntFromObj(interp, objv[3], &total) != TCL_OK) {
1169        return TCL_ERROR;
1170    }
1171    VolumeInterpolator *interpolator =
1172        NanoVis::volRenderer->getVolumeInterpolator();
1173    interpolator->start();
1174    if (interpolator->isStarted()) {
1175        const char *dirName = (objc < 5) ? NULL : Tcl_GetString(objv[4]);
1176        for (int frameNum = 0; frameNum < total; ++frameNum) {
1177            float fraction;
1178
1179            fraction = ((float)frameNum) / (total - 1);
1180            TRACE("fraction : %f", fraction);
1181            interpolator->update(fraction);
1182
1183            int fboOrig;
1184            glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboOrig);
1185
1186            NanoVis::bindOffscreenBuffer();  //enable offscreen render
1187
1188            NanoVis::render();
1189            NanoVis::readScreen();
1190
1191            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboOrig);
1192
1193            /* FIXME: this function requires 4-byte aligned RGB rows,
1194             * but screen buffer is now 1 byte aligned for PPM images.
1195             */
1196            writeBMPFile(frameNum, dirName,
1197                         NanoVis::screenBuffer,
1198                         NanoVis::winWidth, NanoVis::winHeight);
1199        }
1200    }
1201    return TCL_OK;
1202}
1203
1204static int
1205VolumeAnimationClearOp(ClientData clientData, Tcl_Interp *interp, int objc,
1206                       Tcl_Obj *const *objv)
1207{
1208    NanoVis::volRenderer->clearAnimatedVolumeInfo();
1209    return TCL_OK;
1210}
1211
1212static int
1213VolumeAnimationStartOp(ClientData clientData, Tcl_Interp *interp, int objc,
1214                       Tcl_Obj *const *objv)
1215{
1216    NanoVis::volRenderer->startVolumeAnimation();
1217    return TCL_OK;
1218}
1219
1220static int
1221VolumeAnimationStopOp(ClientData clientData, Tcl_Interp *interp, int objc,
1222                      Tcl_Obj *const *objv)
1223{
1224    NanoVis::volRenderer->stopVolumeAnimation();
1225    return TCL_OK;
1226}
1227
1228static int
1229VolumeAnimationVolumesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1230                         Tcl_Obj *const *objv)
1231{
1232    std::vector<Volume *> volumes;
1233    if (GetVolumes(interp, objc - 3, objv + 3, &volumes) != TCL_OK) {
1234        return TCL_ERROR;
1235    }
1236    TRACE("parsing volume data identifier");
1237    NanoVis::VolumeHashmap::iterator itr;
1238    for (itr = NanoVis::volumeTable.begin();
1239         itr != NanoVis::volumeTable.end(); ++itr) {
1240        NanoVis::volRenderer->addAnimatedVolume(itr->second);
1241    }
1242    return TCL_OK;
1243}
1244
1245static CmdSpec volumeAnimationOps[] = {
1246    {"capture",   2, VolumeAnimationCaptureOp,  4, 5, "numframes ?filename?",},
1247    {"clear",     2, VolumeAnimationClearOp,    3, 3, "",},
1248    {"start",     3, VolumeAnimationStartOp,    3, 3, "",},
1249    {"stop",      3, VolumeAnimationStopOp,     3, 3, "",},
1250    {"volumes",   1, VolumeAnimationVolumesOp,  3, 0, "?indices?",},
1251};
1252
1253static int nVolumeAnimationOps = NumCmdSpecs(volumeAnimationOps);
1254
1255static int
1256VolumeAnimationOp(ClientData clientData, Tcl_Interp *interp, int objc,
1257                  Tcl_Obj *const *objv)
1258{
1259    Tcl_ObjCmdProc *proc;
1260
1261    proc = GetOpFromObj(interp, nVolumeAnimationOps, 
1262                        volumeAnimationOps, CMDSPEC_ARG2, objc, objv, 0);
1263    if (proc == NULL) {
1264        return TCL_ERROR;
1265    }
1266    return (*proc) (clientData, interp, objc, objv);
1267}
1268
1269static int
1270VolumeDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1271                    Tcl_Obj *const *objv)
1272{
1273    TRACE("Data Loading");
1274
1275    int nbytes;
1276    if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
1277        return TCL_ERROR;
1278    }
1279    const char *tag = Tcl_GetString(objv[4]);
1280
1281    Rappture::Buffer buf(nbytes);
1282    if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
1283        return TCL_ERROR;
1284    }
1285    const char *bytes = buf.bytes();
1286    size_t nBytes = buf.size();
1287
1288    TRACE("Checking header[%.20s]", bytes);
1289
1290    Volume *volume = NULL;
1291
1292    if ((nBytes > 5) && (strncmp(bytes, "<HDR>", 5) == 0)) {
1293        TRACE("ZincBlende Stream loading...");
1294        volume = (Volume *)ZincBlendeReconstructor::getInstance()->loadFromMemory(bytes);
1295        if (volume == NULL) {
1296            Tcl_AppendResult(interp, "can't get volume instance", (char *)NULL);
1297            return TCL_ERROR;
1298        }
1299        TRACE("finish loading");
1300
1301        NanoVis::VolumeHashmap::iterator itr = NanoVis::volumeTable.find(tag);
1302        if (itr != NanoVis::volumeTable.end()) {
1303            Tcl_AppendResult(interp, "volume \"", tag, "\" already exists.",
1304                             (char *)NULL);
1305            return TCL_ERROR;
1306        }
1307        NanoVis::volumeTable[tag] = volume;
1308        volume->name(tag);
1309    } else if ((nBytes > 14) && (strncmp(bytes, "# vtk DataFile", 14) == 0)) {
1310        TRACE("VTK loading...");
1311        if (nBytes <= 0) {
1312            ERROR("data buffer is empty");
1313            abort();
1314        }
1315#ifdef USE_VTK
1316        volume = load_vtk_volume_stream(tag, bytes, nBytes);
1317#else
1318        std::stringstream fdata;
1319        fdata.write(bytes, nBytes);
1320        volume = load_vtk_volume_stream(tag, fdata);
1321#endif
1322        if (volume == NULL) {
1323            Tcl_AppendResult(interp, "Failed to load VTK file", (char*)NULL);
1324            return TCL_ERROR;
1325        }
1326    } else {
1327#ifdef USE_DX_READER
1328        // **Deprecated** OpenDX format
1329        if ((nBytes > 5) && (strncmp(bytes, "<ODX>", 5) == 0)) {
1330            bytes += 5;
1331            nBytes -= 5;
1332        } else if ((nBytes > 4) && (strncmp(bytes, "<DX>", 4) == 0)) {
1333            bytes += 4;
1334            nBytes -= 4;
1335        }
1336        TRACE("DX loading...");
1337        if (nBytes <= 0) {
1338            ERROR("data buffer is empty");
1339            abort();
1340        }
1341        std::stringstream fdata;
1342        fdata.write(bytes, nBytes);
1343        volume = load_dx_volume_stream(tag, fdata);
1344        if (volume == NULL) {
1345            Tcl_AppendResult(interp, "Failed to load DX file", (char*)NULL);
1346            return TCL_ERROR;
1347        }
1348#else
1349        Tcl_AppendResult(interp, "Loading DX files is not supported by this server", (char*)NULL);
1350        return TCL_ERROR;
1351#endif
1352    }
1353
1354    if (volume != NULL) {
1355        volume->disableCutplane(0);
1356        volume->disableCutplane(1);
1357        volume->disableCutplane(2);
1358        volume->transferFunction(NanoVis::getTransferFunction("default"));
1359        volume->visible(true);
1360
1361        if (Volume::updatePending) {
1362            NanoVis::setVolumeRanges();
1363        }
1364
1365        char info[1024];
1366        // FIXME: strlen(info) is the return value of sprintf
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
1858
1859static int
1860HeightMapLineContourColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1861                            Tcl_Obj *const *objv)
1862{
1863    float rgb[3];
1864    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1865        return TCL_ERROR;
1866    }
1867    std::vector<HeightMap *> imap;
1868    if (GetHeightMaps(interp, objc - 6, objv + 6, &imap) != TCL_OK) {
1869        return TCL_ERROR;
1870    }
1871    std::vector<HeightMap *>::iterator iter;
1872    for (iter = imap.begin(); iter != imap.end(); iter++) {
1873        (*iter)->setLineContourColor(rgb);
1874    }
1875    NanoVis::eventuallyRedraw();
1876    return TCL_OK;
1877}
1878
1879static int
1880HeightMapLineContourVisibleOp(ClientData clientData, Tcl_Interp *interp, 
1881                              int objc, Tcl_Obj *const *objv)
1882{
1883    bool visible;
1884    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1885        return TCL_ERROR;
1886    }
1887    std::vector<HeightMap *> imap;
1888    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1889        return TCL_ERROR;
1890    }
1891    std::vector<HeightMap *>::iterator iter;
1892    for (iter = imap.begin(); iter != imap.end(); iter++) {
1893        (*iter)->setLineContourVisible(visible);
1894    }
1895    NanoVis::eventuallyRedraw();
1896    return TCL_OK;
1897}
1898
1899static CmdSpec heightMapLineContourOps[] = {
1900    {"color",   1, HeightMapLineContourColorOp,   6, 0, "r g b ?heightmapNames...?",},
1901    {"visible", 1, HeightMapLineContourVisibleOp, 4, 0, "bool ?heightmapNames...?",},
1902};
1903static int nHeightMapLineContourOps = NumCmdSpecs(heightMapLineContourOps);
1904
1905static int 
1906HeightMapLineContourOp(ClientData clientData, Tcl_Interp *interp, int objc,
1907                       Tcl_Obj *const *objv)
1908{
1909    Tcl_ObjCmdProc *proc;
1910
1911    proc = GetOpFromObj(interp, nHeightMapLineContourOps,
1912                        heightMapLineContourOps, CMDSPEC_ARG2, objc, objv, 0);
1913    if (proc == NULL) {
1914        return TCL_ERROR;
1915    }
1916    return (*proc) (clientData, interp, objc, objv);
1917}
1918
1919static int
1920HeightMapCullOp(ClientData clientData, Tcl_Interp *interp, int objc,
1921                Tcl_Obj *const *objv)
1922{
1923    RenderContext::CullMode mode;
1924    if (GetCullMode(interp, objv[2], &mode) != TCL_OK) {
1925        return TCL_ERROR;
1926    }
1927    NanoVis::renderContext->setCullMode(mode);
1928    NanoVis::eventuallyRedraw();
1929    return TCL_OK;
1930}
1931
1932static int
1933HeightMapCreateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1934                  Tcl_Obj *const *objv)
1935{
1936    const char *tag = Tcl_GetString(objv[2]);
1937    NanoVis::HeightMapHashmap::iterator itr = NanoVis::heightMapTable.find(tag);
1938    if (itr != NanoVis::heightMapTable.end()) {
1939        Tcl_AppendResult(interp, "heightmap \"", tag, "\" already exists.",
1940                         (char *)NULL);
1941        return TCL_ERROR;
1942    }
1943    /* heightmap create xmin ymin xmax ymax xnum ynum values */
1944    HeightMap *heightMap = CreateHeightMap(clientData, interp, objc - 3, objv + 3);
1945    if (heightMap == NULL) {
1946        return TCL_ERROR;
1947    }
1948    NanoVis::heightMapTable[tag] = heightMap;
1949    NanoVis::eventuallyRedraw();
1950    TRACE("Number of heightmaps=%d", NanoVis::heightMapTable.size());
1951    return TCL_OK;
1952}
1953
1954static int
1955HeightMapLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
1956                  Tcl_Obj *const *objv)
1957{
1958    HeightMap *hmPtr;
1959    if (GetHeightMapFromObj(interp, objv[2], &hmPtr) != TCL_OK) {
1960        return TCL_ERROR;
1961    }
1962    const char *tag;
1963    tag = Tcl_GetString(objv[2]);
1964    TransferFunction *tfPtr;
1965    tfPtr = hmPtr->transferFunction();
1966    if (tfPtr == NULL) {
1967        Tcl_AppendResult(interp, "no transfer function defined for heightmap"
1968                         " \"", tag, "\"", (char*)NULL);
1969        return TCL_ERROR;
1970    }
1971    int w, h;
1972    if ((Tcl_GetIntFromObj(interp, objv[3], &w) != TCL_OK) ||
1973        (Tcl_GetIntFromObj(interp, objv[4], &h) != TCL_OK)) {
1974        return TCL_ERROR;
1975    }
1976    if (HeightMap::updatePending) {
1977        NanoVis::setHeightmapRanges();
1978    }
1979    NanoVis::renderLegend(tfPtr, HeightMap::valueMin, HeightMap::valueMax, 
1980                          w, h, tag);
1981    return TCL_OK;
1982}
1983
1984static int
1985HeightMapPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
1986                   Tcl_Obj *const *objv)
1987{
1988    RenderContext::PolygonMode mode;
1989    if (GetPolygonMode(interp, objv[2], &mode) != TCL_OK) {
1990        return TCL_ERROR;
1991    }
1992    NanoVis::renderContext->setPolygonMode(mode);
1993    NanoVis::eventuallyRedraw();
1994    return TCL_OK;
1995}
1996
1997static int
1998HeightMapShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1999                 Tcl_Obj *const *objv)
2000{
2001    RenderContext::ShadingModel model;
2002    if (GetShadingModel(interp, objv[2], &model) != TCL_OK) {
2003        return TCL_ERROR;
2004    }
2005    NanoVis::renderContext->setShadingModel(model);
2006    NanoVis::eventuallyRedraw();
2007    return TCL_OK;
2008}
2009
2010static int
2011HeightMapTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
2012                     Tcl_Obj *const *objv)
2013{
2014    const char *name;
2015    name = Tcl_GetString(objv[2]);
2016    TransferFunction *tf = NanoVis::getTransferFunction(name);
2017    if (tf == NULL) {
2018        Tcl_AppendResult(interp, "transfer function \"", name,
2019                         "\" is not defined", (char*)NULL);
2020        return TCL_ERROR;
2021    }
2022    std::vector<HeightMap *> imap;
2023    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
2024        return TCL_ERROR;
2025    }
2026    std::vector<HeightMap *>::iterator iter;
2027    for (iter = imap.begin(); iter != imap.end(); iter++) {
2028        (*iter)->transferFunction(tf);
2029    }
2030    NanoVis::eventuallyRedraw();
2031    return TCL_OK;
2032}
2033
2034static int
2035HeightMapOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
2036                   Tcl_Obj *const *objv)
2037{
2038    float opacity;
2039    if (GetFloatFromObj(interp, objv[2], &opacity) != TCL_OK) {
2040        return TCL_ERROR;
2041    }
2042    std::vector<HeightMap *> heightmaps;
2043    if (GetHeightMaps(interp, objc - 3, objv + 3, &heightmaps) != TCL_OK) {
2044        return TCL_ERROR;
2045    }
2046    std::vector<HeightMap *>::iterator iter;
2047    for (iter = heightmaps.begin(); iter != heightmaps.end(); iter++) {
2048        (*iter)->opacity(opacity);
2049    }
2050    NanoVis::eventuallyRedraw();
2051    return TCL_OK;
2052}
2053
2054static CmdSpec heightMapOps[] = {
2055    {"create",       2, HeightMapCreateOp,      10, 10, "heightmapName xmin ymin xmax ymax xnum ynum values",},
2056    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
2057    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
2058    {"legend",       2, HeightMapLegendOp,      5, 5, "heightmapName width height",},
2059    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
2060    {"opacity",      1, HeightMapOpacityOp,     3, 0, "value ?heightmapNames...? ",},
2061    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
2062    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
2063    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?heightmapNames...?",},
2064};
2065static int nHeightMapOps = NumCmdSpecs(heightMapOps);
2066
2067static int
2068HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2069             Tcl_Obj *const *objv)
2070{
2071    Tcl_ObjCmdProc *proc;
2072
2073    proc = GetOpFromObj(interp, nHeightMapOps, heightMapOps,
2074                        CMDSPEC_ARG1, objc, objv, 0);
2075    if (proc == NULL) {
2076        return TCL_ERROR;
2077    }
2078    return (*proc) (clientData, interp, objc, objv);
2079}
2080
2081static int
2082GridAxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2083                Tcl_Obj *const *objv)
2084{
2085    float r, g, b, a;
2086    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2087        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2088        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2089        return TCL_ERROR;
2090    }
2091    a = 1.0f;
2092    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2093        return TCL_ERROR;
2094    }
2095    if (NanoVis::grid) {
2096        NanoVis::grid->setAxisColor(r, g, b, a);
2097    }
2098    return TCL_OK;
2099}
2100
2101static int
2102GridAxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2103               Tcl_Obj *const *objv)
2104{
2105    int axis;
2106    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
2107        return TCL_ERROR;
2108    }
2109    if (NanoVis::grid != NULL) {
2110        Axis *axisPtr;
2111
2112        axisPtr = NULL;     /* Suppress compiler warning. */
2113        switch (axis) {
2114        case 0: axisPtr = &NanoVis::grid->xAxis; break;
2115        case 1: axisPtr = &NanoVis::grid->yAxis; break;
2116        case 2: axisPtr = &NanoVis::grid->zAxis; break;
2117        }
2118        axisPtr->name(Tcl_GetString(objv[3]));
2119        axisPtr->units(Tcl_GetString(objv[4]));
2120    }
2121    return TCL_OK;
2122}
2123
2124static int
2125GridLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2126                Tcl_Obj *const *objv)
2127{
2128    float r, g, b, a;
2129    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2130        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2131        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2132        return TCL_ERROR;
2133    }
2134    a = 1.0f;
2135    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2136        return TCL_ERROR;
2137    }
2138    if (NanoVis::grid) {
2139        NanoVis::grid->setLineColor(r, g, b, a);
2140    }
2141    return TCL_OK;
2142}
2143
2144static int
2145GridVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc, 
2146              Tcl_Obj *const *objv)
2147{
2148    bool visible;
2149    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2150        return TCL_ERROR;
2151    }
2152    NanoVis::grid->setVisible(visible);
2153    return TCL_OK;
2154}
2155
2156static CmdSpec gridOps[] = {
2157    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2158    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2159    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2160    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2161};
2162static int nGridOps = NumCmdSpecs(gridOps);
2163
2164static int
2165GridCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2166        Tcl_Obj *const *objv)
2167{
2168    Tcl_ObjCmdProc *proc;
2169
2170    proc = GetOpFromObj(interp, nGridOps, gridOps,
2171                        CMDSPEC_ARG1, objc, objv, 0);
2172    if (proc == NULL) {
2173        return TCL_ERROR;
2174    }
2175    return (*proc) (clientData, interp, objc, objv);
2176}
2177
2178static int
2179AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2180        Tcl_Obj *const *objv)
2181{
2182    if (objc < 2) {
2183        Tcl_AppendResult(interp, "wrong # args: should be \"",
2184                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2185        return TCL_ERROR;
2186    }
2187    const char *string = Tcl_GetString(objv[1]);
2188    char c = string[0];
2189    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2190        bool visible;
2191
2192        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2193            return TCL_ERROR;
2194        }
2195        NanoVis::orientationIndicator->setVisible(visible);
2196    } else {
2197        Tcl_AppendResult(interp, "bad axis option \"", string,
2198                         "\": should be visible", (char*)NULL);
2199        return TCL_ERROR;
2200    }
2201    return TCL_OK;
2202}
2203
2204static int
2205ImageFlushCmd(ClientData clientData, Tcl_Interp *interp, int objc, 
2206              Tcl_Obj *const *objv)
2207{
2208    lastCmdStatus = TCL_BREAK;
2209    return TCL_OK;
2210}
2211
2212/**
2213 * \brief Execute commands from client in Tcl interpreter
2214 *
2215 * In this threaded model, the select call is for event compression.  We
2216 * want to execute render server commands as long as they keep coming. 
2217 * This lets us execute a stream of many commands but render once.  This
2218 * benefits camera movements, screen resizing, and opacity changes
2219 * (using a slider on the client).  The down side is you don't render
2220 * until there's a lull in the command stream.  If the client needs an
2221 * image, it can issue an "imgflush" command.  That breaks us out of the
2222 * read loop.
2223 */
2224int
2225nv::processCommands(Tcl_Interp *interp,
2226                    ReadBuffer *inBufPtr, int fdOut)
2227{
2228    int ret = 1;
2229    int status = TCL_OK;
2230
2231    Tcl_DString command;
2232    Tcl_DStringInit(&command);
2233    fd_set readFds;
2234    struct timeval tv, *tvPtr;
2235
2236    FD_ZERO(&readFds);
2237    FD_SET(inBufPtr->file(), &readFds);
2238    tvPtr = NULL;                       /* Wait for the first read. This is so
2239                                         * that we don't spin when no data is
2240                                         * available. */
2241    while (inBufPtr->isLineAvailable() || 
2242           (select(inBufPtr->file()+1, &readFds, NULL, NULL, tvPtr) > 0)) {
2243        size_t numBytes;
2244        unsigned char *buffer;
2245
2246        /* A short read is treated as an error here because we assume that we
2247         * will always get commands line by line. */
2248        if (inBufPtr->getLine(&numBytes, &buffer) != ReadBuffer::OK) {
2249            /* Terminate the server if we can't communicate with the client
2250             * anymore. */
2251            if (inBufPtr->status() == ReadBuffer::ENDFILE) {
2252                TRACE("Exiting server on EOF from client");
2253                return -1;
2254            } else {
2255                ERROR("Exiting server, failed to read from client: %s",
2256                      strerror(errno));
2257                return -1;
2258            }
2259        }
2260        Tcl_DStringAppend(&command, (char *)buffer, numBytes);
2261        if (Tcl_CommandComplete(Tcl_DStringValue(&command))) {
2262            struct timeval start, finish;
2263            gettimeofday(&start, NULL);
2264            status = ExecuteCommand(interp, &command);
2265            gettimeofday(&finish, NULL);
2266            g_stats.cmdTime += (MSECS_ELAPSED(start, finish) / 1.0e+3);
2267            g_stats.nCommands++;
2268            if (status == TCL_BREAK) {
2269                return 1;               /* This was caused by a "imgflush"
2270                                         * command. Break out of the read loop
2271                                         * and allow a new image to be
2272                                         * rendered. */
2273            } else { //if (status != TCL_OK) {
2274                ret = 0;
2275                if (handleError(interp, status, fdOut) < 0) {
2276                    return -1;
2277                }
2278            }
2279        }
2280
2281        tv.tv_sec = tv.tv_usec = 0L;    /* On successive reads, we break out
2282                                         * if no data is available. */
2283        FD_SET(inBufPtr->file(), &readFds);
2284        tvPtr = &tv;
2285    }
2286
2287    return ret;
2288}
2289
2290/**
2291 * \brief Send error message to client socket
2292 */
2293int
2294nv::handleError(Tcl_Interp *interp, int status, int fdOut)
2295{
2296    const char *string;
2297    int nBytes;
2298
2299    if (status != TCL_OK) {
2300        string = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
2301        nBytes = strlen(string);
2302        if (nBytes > 0) {
2303            TRACE("status=%d errorInfo=(%s)", status, string);
2304
2305            std::ostringstream oss;
2306            oss << "nv>viserror -type internal_error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string;
2307            std::string ostr = oss.str();
2308            nBytes = ostr.length();
2309
2310#ifdef USE_THREADS
2311            queueResponse(ostr.c_str(), nBytes, Response::VOLATILE, Response::ERROR);
2312#else
2313            if (write(fdOut, oss.str().c_str(), nBytes) < 0) {
2314                ERROR("write failed: %s", strerror(errno));
2315                return -1;
2316            }
2317#endif
2318        }
2319    }
2320
2321    std::string msg = getUserMessages();
2322    nBytes = msg.length();
2323    if (nBytes > 0) {
2324        string = msg.c_str();
2325        TRACE("userError=(%s)", string);
2326
2327        std::ostringstream oss;
2328        oss << "nv>viserror -type error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string;
2329        std::string ostr = oss.str();
2330        nBytes = ostr.length();
2331
2332#ifdef USE_THREADS
2333        queueResponse(ostr.c_str(), nBytes, Response::VOLATILE, Response::ERROR);
2334#else
2335        if (write(fdOut, ostr.c_str(), nBytes) < 0) {
2336            ERROR("write failed: %s", strerror(errno));
2337            return -1;
2338        }
2339#endif
2340        clearUserMessages();
2341    }
2342
2343    return 0;
2344}
2345
2346void
2347nv::initTcl(Tcl_Interp *interp, ClientData clientData)
2348{
2349    /*
2350     * Ideally the connection is authenticated by nanoscale.  I still like the
2351     * idea of creating a non-safe master interpreter with a safe slave
2352     * interpreter.  Alias all the nanovis commands in the slave. That way we
2353     * can still run Tcl code within nanovis.  The eventual goal is to create
2354     * a test harness through the interpreter for nanovis.
2355     */
2356
2357    Tcl_MakeSafe(interp);
2358
2359    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        clientData, NULL);
2360    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      clientData, NULL);
2361    Tcl_CreateObjCommand(interp, "clientinfo",  ClientInfoCmd,  clientData, NULL);
2362    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    clientData, NULL);
2363    FlowCmdInitProc(interp, clientData);
2364    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        clientData, NULL);
2365    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   clientData, NULL);
2366    Tcl_CreateObjCommand(interp, "imgflush",    ImageFlushCmd,  clientData, NULL);
2367    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      clientData, NULL);
2368    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      clientData, NULL);
2369    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    clientData, NULL);
2370    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   clientData, NULL);
2371    Tcl_CreateObjCommand(interp, "up",          UpCmd,          clientData, NULL);
2372    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      clientData, NULL);
2373
2374    // create a default transfer function
2375    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2376        WARN("bad default transfer function:\n%s", 
2377             Tcl_GetStringResult(interp));
2378    }
2379}
Note: See TracBrowser for help on using the repository browser.