source: nanovis/branches/1.2/Command.cpp @ 5046

Last change on this file since 5046 was 4937, checked in by ldelgass, 9 years ago

merge threading

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