source: nanovis/trunk/Command.cpp @ 4880

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

Fix leak in sendDataToClient method (when USE_THREADS is enabled)

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