source: nanovis/trunk/Command.cpp @ 4923

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

Sync with release branch

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