source: trunk/packages/vizservers/nanovis/Command.cpp @ 4422

Last change on this file since 4422 was 4422, checked in by ldelgass, 7 years ago

Fix crash bug in user errors: was returning pointer to temporary. Return a
copy of string object from getUserMessages instead of pointer.

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