source: nanovis/trunk/Command.cpp @ 4894

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

sync with release branch

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