source: nanovis/branches/1.1/Command.cpp @ 4829

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

Add stub imgflush command

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