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

Last change on this file since 2953 was 2953, checked in by ldelgass, 10 years ago

Remove unused global origin, make default transfer function a bit more
sensible (used to have full opacity at 0). Fix HeightMap? dtor to use delete[]
instead of free() on array allocated with new[]. Document data response in
protocol.

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