source: nanovis/trunk/Command.cpp @ 4895

Last change on this file since 4895 was 4895, checked in by ldelgass, 6 years ago

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