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

Last change on this file since 2974 was 2974, checked in by ldelgass, 12 years ago

Remove unused volume axis/bbox labeling from VolumeRenderer?. This feature is
now provided by the Grid. Also remove unused volume/slice mode flags in
VolumeRenderer? since it now uses cutplane information from each Volume object
instead.

  • Property svn:eol-style set to native
File size: 70.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
1492VolumeStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1493              Tcl_Obj *const *objv)
1494{
1495    bool state;
1496    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1497        return TCL_ERROR;
1498    }
1499    std::vector<Volume *> ivol;
1500    if (GetVolumes(interp, objc - 3, objv + 3, &ivol) != TCL_OK) {
1501        return TCL_ERROR;
1502    }
1503    std::vector<Volume *>::iterator iter;
1504    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1505        (*iter)->visible(state);
1506    }
1507    return TCL_OK;
1508}
1509
1510static int
1511VolumeTestOp(ClientData clientData, Tcl_Interp *interp, int objc,
1512             Tcl_Obj *const *objv)
1513{
1514    // Find the first volume in the vector.
1515    Tcl_HashEntry *hPtr;
1516    Tcl_HashSearch iter;
1517    hPtr = Tcl_FirstHashEntry(&NanoVis::volumeTable, &iter);
1518    if (hPtr != NULL) {
1519        Volume *volPtr;
1520        volPtr = (Volume *)Tcl_GetHashValue(hPtr);
1521        volPtr->dataEnabled(false);
1522        volPtr->visible(false);
1523    }
1524    return TCL_OK;
1525}
1526
1527static Rappture::CmdSpec volumeOps[] = {
1528    {"animation", 2, VolumeAnimationOp,   3, 0, "oper ?args?",},
1529    {"data",      2, VolumeDataOp,        3, 0, "oper ?args?",},
1530    {"delete",    2, VolumeDeleteOp,      3, 0, "?name...?",},
1531    {"exists",    1, VolumeExistsOp,      3, 3, "name",},
1532    {"names",     1, VolumeNamesOp,       2, 3, "?pattern?",},
1533    {"outline",   1, VolumeOutlineOp,     3, 0, "oper ?args?",},
1534    {"shading",   2, VolumeShadingOp,     3, 0, "oper ?args?",},
1535    {"state",     2, VolumeStateOp,       3, 0, "bool ?indices?",},
1536    {"test2",     1, VolumeTestOp,        2, 2, "",},
1537};
1538static int nVolumeOps = NumCmdSpecs(volumeOps);
1539
1540/*
1541 * ----------------------------------------------------------------------
1542 * CLIENT COMMAND:
1543 *   volume data state on|off ?<volumeId> ...?
1544 *   volume outline state on|off ?<volumeId> ...?
1545 *   volume outline color on|off ?<volumeId> ...?
1546 *   volume shading transfunc <name> ?<volumeId> ...?
1547 *   volume shading diffuse <value> ?<volumeId> ...?
1548 *   volume shading specular <value> ?<volumeId> ...?
1549 *   volume shading opacity <value> ?<volumeId> ...?
1550 *   volume state on|off ?<volumeId> ...?
1551 *
1552 * Clients send these commands to manipulate the volumes.
1553 * ----------------------------------------------------------------------
1554 */
1555static int
1556VolumeCmd(ClientData clientData, Tcl_Interp *interp, int objc,
1557          Tcl_Obj *const *objv)
1558{
1559    Tcl_ObjCmdProc *proc;
1560
1561    proc = Rappture::GetOpFromObj(interp, nVolumeOps, volumeOps,
1562        Rappture::CMDSPEC_ARG1, objc, objv, 0);
1563    if (proc == NULL) {
1564        return TCL_ERROR;
1565    }
1566    return (*proc) (clientData, interp, objc, objv);
1567}
1568
1569// ========================= VOLUME END ==================================
1570
1571static int
1572HeightMapDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1573                       Tcl_Obj *const *objv)
1574{
1575    int nBytes;
1576    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
1577        return TCL_ERROR;
1578    }
1579    const char *tag;
1580    tag = Tcl_GetString(objv[4]);
1581    int isNew;
1582    Tcl_HashEntry *hPtr;
1583
1584    Rappture::Buffer buf;
1585    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
1586        return TCL_ERROR;
1587    }
1588    Rappture::Unirect2d data(1);
1589    if (data.parseBuffer(interp, buf) != TCL_OK) {
1590        return TCL_ERROR;
1591    }
1592    if (data.nValues() == 0) {
1593        Tcl_AppendResult(interp, "no data found in stream", (char *)NULL);
1594        return TCL_ERROR;
1595    }
1596    if (!data.isInitialized()) {
1597        return TCL_ERROR;
1598    }
1599    HeightMap* hmPtr;
1600    hPtr = Tcl_CreateHashEntry(&NanoVis::heightmapTable, tag, &isNew);
1601    if (isNew) {
1602        hmPtr = new HeightMap();
1603        Tcl_SetHashValue(hPtr, hmPtr);
1604    } else {
1605        hmPtr = (HeightMap *)Tcl_GetHashValue(hPtr);
1606    }
1607    TRACE("Number of heightmaps=%d\n", NanoVis::heightmapTable.numEntries);
1608    // Must set units before the heights.
1609    hmPtr->xAxis.units(data.xUnits());
1610    hmPtr->yAxis.units(data.yUnits());
1611    hmPtr->zAxis.units(data.vUnits());
1612    hmPtr->wAxis.units(data.yUnits());
1613    hmPtr->setHeight(data.xMin(), data.yMin(), data.xMax(), data.yMax(),
1614                     data.xNum(), data.yNum(), data.transferValues());
1615    hmPtr->transferFunction(NanoVis::getTransfunc("default"));
1616    hmPtr->setVisible(true);
1617    hmPtr->setLineContourVisible(true);
1618    NanoVis::eventuallyRedraw();
1619    return TCL_OK;
1620}
1621
1622static int
1623HeightMapDataVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1624                       Tcl_Obj *const *objv)
1625{
1626    bool visible;
1627    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1628        return TCL_ERROR;
1629    }
1630    std::vector<HeightMap *> imap;
1631    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1632        return TCL_ERROR;
1633    }
1634    std::vector<HeightMap *>::iterator iter;
1635    for (iter = imap.begin(); iter != imap.end(); iter++) {
1636        (*iter)->setVisible(visible);
1637    }
1638    NanoVis::eventuallyRedraw();
1639    return TCL_OK;
1640}
1641
1642static Rappture::CmdSpec heightMapDataOps[] = {
1643    {"follows",  1, HeightMapDataFollowsOp, 5, 5, "size tag",},
1644    {"visible",  1, HeightMapDataVisibleOp, 4, 0, "bool ?indices?",},
1645};
1646static int nHeightMapDataOps = NumCmdSpecs(heightMapDataOps);
1647
1648static int
1649HeightMapDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1650                Tcl_Obj *const *objv)
1651{
1652    Tcl_ObjCmdProc *proc;
1653
1654    proc = Rappture::GetOpFromObj(interp, nHeightMapDataOps, heightMapDataOps,
1655                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1656    if (proc == NULL) {
1657        return TCL_ERROR;
1658    }
1659    return (*proc) (clientData, interp, objc, objv);
1660}
1661
1662
1663static int
1664HeightMapLineContourColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1665                            Tcl_Obj *const *objv)
1666{
1667    float rgb[3];
1668    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1669        return TCL_ERROR;
1670    }
1671    std::vector<HeightMap *> imap;
1672    if (GetHeightMaps(interp, objc - 6, objv + 6, &imap) != TCL_OK) {
1673        return TCL_ERROR;
1674    }
1675    std::vector<HeightMap *>::iterator iter;
1676    for (iter = imap.begin(); iter != imap.end(); iter++) {
1677        (*iter)->setLineContourColor(rgb);
1678    }
1679    NanoVis::eventuallyRedraw();
1680    return TCL_OK;
1681}
1682
1683static int
1684HeightMapLineContourVisibleOp(ClientData clientData, Tcl_Interp *interp,
1685                              int objc, Tcl_Obj *const *objv)
1686{
1687    bool visible;
1688    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
1689        return TCL_ERROR;
1690    }
1691    std::vector<HeightMap *> imap;
1692    if (GetHeightMaps(interp, objc - 4, objv + 4, &imap) != TCL_OK) {
1693        return TCL_ERROR;
1694    }
1695    std::vector<HeightMap *>::iterator iter;
1696    for (iter = imap.begin(); iter != imap.end(); iter++) {
1697        (*iter)->setLineContourVisible(visible);
1698    }
1699    NanoVis::eventuallyRedraw();
1700    return TCL_OK;
1701}
1702
1703static Rappture::CmdSpec heightMapLineContourOps[] = {
1704    {"color",   1, HeightMapLineContourColorOp,   4, 4, "length",},
1705    {"visible", 1, HeightMapLineContourVisibleOp, 4, 0, "bool ?indices?",},
1706};
1707static int nHeightMapLineContourOps = NumCmdSpecs(heightMapLineContourOps);
1708
1709static int
1710HeightMapLineContourOp(ClientData clientData, Tcl_Interp *interp, int objc,
1711                       Tcl_Obj *const *objv)
1712{
1713    Tcl_ObjCmdProc *proc;
1714
1715    proc = Rappture::GetOpFromObj(interp, nHeightMapLineContourOps,
1716        heightMapLineContourOps, Rappture::CMDSPEC_ARG2, objc, objv, 0);
1717    if (proc == NULL) {
1718        return TCL_ERROR;
1719    }
1720    return (*proc) (clientData, interp, objc, objv);
1721}
1722
1723static int
1724HeightMapCullOp(ClientData clientData, Tcl_Interp *interp, int objc,
1725                Tcl_Obj *const *objv)
1726{
1727    graphics::RenderContext::CullMode mode;
1728    if (GetCullMode(interp, objv[2], &mode) != TCL_OK) {
1729        return TCL_ERROR;
1730    }
1731    NanoVis::renderContext->setCullMode(mode);
1732    NanoVis::eventuallyRedraw();
1733    return TCL_OK;
1734}
1735
1736static int
1737HeightMapCreateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1738                  Tcl_Obj *const *objv)
1739{
1740    const char *tag;
1741    tag = Tcl_GetString(objv[2]);
1742    Tcl_HashEntry *hPtr;
1743    int isNew;
1744    hPtr = Tcl_CreateHashEntry(&NanoVis::heightmapTable, tag, &isNew);
1745    if (!isNew) {
1746        Tcl_AppendResult(interp, "heightmap \"", tag, "\" already exists.",
1747                         (char *)NULL);
1748        return TCL_ERROR;
1749    }
1750    HeightMap *hmPtr;
1751    /* heightmap create xmin ymin xmax ymax xnum ynum values */
1752    hmPtr = CreateHeightMap(clientData, interp, objc - 3, objv + 3);
1753    if (hmPtr == NULL) {
1754        return TCL_ERROR;
1755    }
1756    Tcl_SetHashValue(hPtr, hmPtr);
1757    Tcl_SetStringObj(Tcl_GetObjResult(interp), tag, -1);;
1758    NanoVis::eventuallyRedraw();
1759    TRACE("Number of heightmaps=%d\n", NanoVis::heightmapTable.numEntries);
1760    return TCL_OK;
1761}
1762
1763static int
1764HeightMapLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
1765                  Tcl_Obj *const *objv)
1766{
1767    HeightMap *hmPtr;
1768    if (GetHeightMapFromObj(interp, objv[2], &hmPtr) != TCL_OK) {
1769        return TCL_ERROR;
1770    }
1771    const char *tag;
1772    tag = Tcl_GetString(objv[2]);
1773    TransferFunction *tfPtr;
1774    tfPtr = hmPtr->transferFunction();
1775    if (tfPtr == NULL) {
1776        Tcl_AppendResult(interp, "no transfer function defined for heightmap"
1777                         " \"", tag, "\"", (char*)NULL);
1778        return TCL_ERROR;
1779    }
1780    int w, h;
1781    if ((Tcl_GetIntFromObj(interp, objv[3], &w) != TCL_OK) ||
1782        (Tcl_GetIntFromObj(interp, objv[4], &h) != TCL_OK)) {
1783        return TCL_ERROR;
1784    }
1785    if (HeightMap::updatePending) {
1786        NanoVis::setHeightmapRanges();
1787    }
1788    NanoVis::renderLegend(tfPtr, HeightMap::valueMin, HeightMap::valueMax,
1789                          w, h, tag);
1790    return TCL_OK;
1791}
1792
1793static int
1794HeightMapPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
1795                   Tcl_Obj *const *objv)
1796{
1797    graphics::RenderContext::PolygonMode mode;
1798    if (GetPolygonMode(interp, objv[2], &mode) != TCL_OK) {
1799        return TCL_ERROR;
1800    }
1801    NanoVis::renderContext->setPolygonMode(mode);
1802    NanoVis::eventuallyRedraw();
1803    return TCL_OK;
1804}
1805
1806static int
1807HeightMapShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1808                 Tcl_Obj *const *objv)
1809{
1810    graphics::RenderContext::ShadingModel model;
1811    if (GetShadingModel(interp, objv[2], &model) != TCL_OK) {
1812        return TCL_ERROR;
1813    }
1814    NanoVis::renderContext->setShadingModel(model);
1815    NanoVis::eventuallyRedraw();
1816    return TCL_OK;
1817}
1818
1819
1820static int
1821HeightMapTopView(ClientData data, Tcl_Interp *interp, int objc,
1822                Tcl_Obj *const *objv)
1823{
1824
1825    // the variables below should be reassigned
1826    int image_width = 512;
1827    int image_height = 512;
1828    HeightMap* heightmap = 0;
1829
1830    // HELP ME
1831    // GEORGE
1832
1833    NanoVis::render2dContour(heightmap, image_width, image_height);
1834    NanoVis::eventuallyRedraw();
1835    return TCL_OK;
1836}
1837
1838static int
1839HeightMapTestOp(ClientData clientData, Tcl_Interp *interp, int objc,
1840                Tcl_Obj *const *objv)
1841{
1842    srand((unsigned)time(NULL));
1843
1844    int size = 20 * 20;
1845    double sigma = 5.0;
1846    double mean = exp(0.0) / (sigma * sqrt(2.0));
1847    float* data = (float*) malloc(sizeof(float) * size);
1848
1849    float x, y;
1850    for (int i = 0; i < size; ++i) {
1851        x = - 10 + i%20;
1852        y = - 10 + (i/20);
1853        data[i] = exp(- (x * y)/(2 * sigma * sigma)) /
1854            (sigma * sqrt(2.0)) / mean * 2 + 1000;
1855        //data[i] = ((float)rand()) / RAND_MAX * 1.0;
1856    }
1857
1858    HeightMap* hmPtr = new HeightMap();
1859    float minx = 0.0f;
1860    float maxx = 1.0f;
1861    float miny = 0.5f;
1862    float maxy = 3.5f;
1863    hmPtr->setHeight(minx, miny, maxx, maxy, 20, 20, data);
1864    hmPtr->transferFunction(NanoVis::getTransfunc("default"));
1865    hmPtr->setVisible(true);
1866    hmPtr->setLineContourVisible(true);
1867    NanoVis::grid->setVisible(true);
1868    Tcl_HashEntry *hPtr;
1869    int isNew;
1870    hPtr = Tcl_CreateHashEntry(&NanoVis::heightmapTable, "test", &isNew);
1871    if (!isNew) {
1872        Tcl_AppendResult(interp, "heightmap \"test\" already exists.",
1873                         (char *)NULL);
1874        return TCL_ERROR;
1875    }
1876    Tcl_SetHashValue(hPtr, hmPtr);
1877    int image_width = 512;
1878    int image_height = 512;
1879
1880    NanoVis::render2dContour(hmPtr, image_width, image_height);
1881
1882    return TCL_OK;
1883}
1884
1885static int
1886HeightMapTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1887                     Tcl_Obj *const *objv)
1888{
1889    const char *name;
1890    name = Tcl_GetString(objv[2]);
1891    TransferFunction *tfPtr;
1892    tfPtr = NanoVis::getTransfunc(name);
1893    if (tfPtr == NULL) {
1894        Tcl_AppendResult(interp, "transfer function \"", name,
1895                         "\" is not defined", (char*)NULL);
1896        return TCL_ERROR;
1897    }
1898    std::vector<HeightMap *> imap;
1899    if (GetHeightMaps(interp, objc - 3, objv + 3, &imap) != TCL_OK) {
1900        return TCL_ERROR;
1901    }
1902    std::vector<HeightMap *>::iterator iter;
1903    for (iter = imap.begin(); iter != imap.end(); iter++) {
1904        (*iter)->transferFunction(tfPtr);
1905    }
1906    NanoVis::eventuallyRedraw();
1907    return TCL_OK;
1908}
1909
1910
1911static int
1912HeightMapOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1913                   Tcl_Obj *const *objv)
1914{
1915    float opacity;
1916    if (GetFloatFromObj(interp, objv[2], &opacity) != TCL_OK) {
1917        return TCL_ERROR;
1918    }
1919    std::vector<HeightMap *> heightmaps;
1920    if (GetHeightMaps(interp, objc - 3, objv + 3, &heightmaps) != TCL_OK) {
1921        return TCL_ERROR;
1922    }
1923    std::vector<HeightMap *>::iterator iter;
1924    for (iter = heightmaps.begin(); iter != heightmaps.end(); iter++) {
1925        (*iter)->opacity(opacity);
1926    }
1927    NanoVis::eventuallyRedraw();
1928    return TCL_OK;
1929}
1930
1931static Rappture::CmdSpec heightMapOps[] = {
1932    {"create",       2, HeightMapCreateOp,      10, 10, "tag xmin ymin xmax ymax xnum ynum values",},
1933    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
1934    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
1935    {"legend",       2, HeightMapLegendOp,      5, 5, "index width height",},
1936    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
1937    {"opacity",      1, HeightMapOpacityOp,     3, 0, "value ?heightmap...? ",},
1938    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
1939    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
1940    {"test",         2, HeightMapTestOp,        2, 2, "",},
1941    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?heightmap...?",},
1942
1943    // HELP ME
1944    // GOERGE
1945    {"topview",      2, HeightMapTopView,     2, 2, "",},
1946};
1947static int nHeightMapOps = NumCmdSpecs(heightMapOps);
1948
1949static int
1950HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc,
1951             Tcl_Obj *const *objv)
1952{
1953    Tcl_ObjCmdProc *proc;
1954
1955    proc = Rappture::GetOpFromObj(interp, nHeightMapOps, heightMapOps,
1956                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
1957    if (proc == NULL) {
1958        return TCL_ERROR;
1959    }
1960    return (*proc) (clientData, interp, objc, objv);
1961}
1962
1963static int
1964GridAxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1965                Tcl_Obj *const *objv)
1966{
1967    float r, g, b, a;
1968    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
1969        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
1970        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
1971        return TCL_ERROR;
1972    }
1973    a = 1.0f;
1974    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
1975        return TCL_ERROR;
1976    }
1977    if (NanoVis::grid) {
1978        NanoVis::grid->setAxisColor(r, g, b, a);
1979    }
1980    return TCL_OK;
1981}
1982
1983static int
1984GridAxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc,
1985               Tcl_Obj *const *objv)
1986{
1987    int axis;
1988    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
1989        return TCL_ERROR;
1990    }
1991    if (NanoVis::grid != NULL) {
1992        Axis *axisPtr;
1993
1994        axisPtr = NULL;     /* Suppress compiler warning. */
1995        switch (axis) {
1996        case 0: axisPtr = &NanoVis::grid->xAxis; break;
1997        case 1: axisPtr = &NanoVis::grid->yAxis; break;
1998        case 2: axisPtr = &NanoVis::grid->zAxis; break;
1999        }
2000        axisPtr->name(Tcl_GetString(objv[3]));
2001        axisPtr->units(Tcl_GetString(objv[4]));
2002    }
2003    return TCL_OK;
2004}
2005
2006static int
2007GridLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2008                Tcl_Obj *const *objv)
2009{
2010    float r, g, b, a;
2011    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2012        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2013        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2014        return TCL_ERROR;
2015    }
2016    a = 1.0f;
2017    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2018        return TCL_ERROR;
2019    }
2020    if (NanoVis::grid) {
2021        NanoVis::grid->setLineColor(r, g, b, a);
2022    }
2023    return TCL_OK;
2024}
2025
2026static int
2027GridVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2028              Tcl_Obj *const *objv)
2029{
2030    bool visible;
2031    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2032        return TCL_ERROR;
2033    }
2034    NanoVis::grid->setVisible(visible);
2035    return TCL_OK;
2036}
2037
2038static Rappture::CmdSpec gridOps[] = {
2039    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2040    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2041    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2042    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2043};
2044static int nGridOps = NumCmdSpecs(gridOps);
2045
2046static int
2047GridCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2048        Tcl_Obj *const *objv)
2049{
2050    Tcl_ObjCmdProc *proc;
2051
2052    proc = Rappture::GetOpFromObj(interp, nGridOps, gridOps,
2053        Rappture::CMDSPEC_ARG1, objc, objv, 0);
2054    if (proc == NULL) {
2055        return TCL_ERROR;
2056    }
2057    return (*proc) (clientData, interp, objc, objv);
2058}
2059
2060static int
2061AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2062        Tcl_Obj *const *objv)
2063{
2064    if (objc < 2) {
2065        Tcl_AppendResult(interp, "wrong # args: should be \"",
2066                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2067        return TCL_ERROR;
2068    }
2069    const char *string = Tcl_GetString(objv[1]);
2070    char c = string[0];
2071    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2072        bool visible;
2073
2074        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2075            return TCL_ERROR;
2076        }
2077        NanoVis::axisOn = visible;
2078    } else {
2079        Tcl_AppendResult(interp, "bad axis option \"", string,
2080                         "\": should be visible", (char*)NULL);
2081        return TCL_ERROR;
2082    }
2083    return TCL_OK;
2084}
2085
2086#if PLANE_CMD
2087static int
2088PlaneAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
2089           Tcl_Obj *const *objv)
2090{
2091    TRACE("load plane for 2D visualization command\n");
2092
2093    int index, w, h;
2094    if (objc != 4) {
2095        Tcl_AppendResult(interp, "wrong # args: should be \"",
2096            Tcl_GetString(objv[0]), " plane_index w h \"", (char*)NULL);
2097        return TCL_ERROR;
2098    }
2099    if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) {
2100        return TCL_ERROR;
2101    }
2102    if (index >= NanoVis::numPlanes) {
2103        Tcl_AppendResult(interp, "Invalid plane_index", (char*)NULL);
2104        return TCL_ERROR;
2105    }
2106    if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) {
2107        return TCL_ERROR;
2108    }
2109    if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) {
2110        return TCL_ERROR;
2111    }
2112
2113    //Now read w*h*4 bytes. The server expects the plane to be a stream of
2114    //floats
2115    char *tmp = new char[int(w*h*sizeof(float))];
2116    if (tmp == NULL) {
2117        Tcl_AppendResult(interp, "can't allocate stream data", (char *)NULL);
2118        return TCL_ERROR;
2119    }
2120    bzero(tmp, w*h*4);
2121    int status = read(fileno(NanoVis::stdin), tmp, w*h*sizeof(float));
2122    if (status <= 0) {
2123        delete[] tmp;
2124        Tcl_AppendResult(interp, "Failed to read image data for plane", (char*)NULL);
2125        return TCL_ERROR;
2126    }
2127    NanoVis::plane[index] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, (float*)tmp);
2128    delete[] tmp;
2129    return TCL_OK;
2130}
2131
2132static int
2133PlaneLinkOp(ClientData clientData, Tcl_Interp *interp, int objc,
2134            Tcl_Obj *const *objv)
2135{
2136    TRACE("link the plane to the 2D renderer command\n");
2137
2138    int plane_index;
2139
2140    if (objc != 3) {
2141        Tcl_AppendResult(interp, "wrong # args: should be \"",
2142            Tcl_GetString(objv[0]), " plane_index transfunc_name \"", (char*)NULL);
2143        return TCL_ERROR;
2144    }
2145    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2146        return TCL_ERROR;
2147    }
2148    if (plane_index >= NanoVis::numPlanes) {
2149        Tcl_AppendResult(interp, "Invalid plane_index", (char*)NULL);
2150        return TCL_ERROR;
2151    }
2152    NanoVis::planeRenderer->addPlane(NanoVis::plane[plane_index],
2153                                     NanoVis::getTransfunc(Tcl_GetString(objv[2])));
2154    return TCL_OK;
2155}
2156
2157//Enable a 2D plane for render
2158//The plane_index is the index mantained in the 2D plane renderer
2159static int
2160PlaneEnableOp(ClientData clientData, Tcl_Interp *interp, int objc,
2161              Tcl_Obj *const *objv)
2162{
2163    TRACE("enable a plane so the 2D renderer can render it command\n");
2164
2165    if (objc != 3) {
2166        Tcl_AppendResult(interp, "wrong # args: should be \"",
2167            Tcl_GetString(objv[0]), " plane_index mode \"", (char*)NULL);
2168        return TCL_ERROR;
2169    }
2170    int plane_index;
2171    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2172        return TCL_ERROR;
2173    }
2174    if (plane_index >= NanoVis::numPlanes) {
2175        Tcl_AppendResult(interp, "Invalid plane_index", (char*)NULL);
2176        return TCL_ERROR;
2177    } else if (plane_index < 0) {
2178        plane_index = -1;
2179    }
2180
2181    NanoVis::planeRenderer->setActivePlane(plane_index);
2182    return TCL_OK;
2183}
2184
2185static Rappture::CmdSpec planeOps[] = {
2186    {"active",     2, PlaneEnableOp,    3, 3, "planeIdx",},
2187    {"add",        2, PlaneAddOp,       5, 5, "planeIdx width height",},
2188    {"link",       1, PlaneLinkOp,      4, 4, "planeIdx transfunc_name",},
2189};
2190static int nPlaneOps = NumCmdSpecs(planeOps);
2191
2192static int
2193PlaneCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2194         Tcl_Obj *const *objv)
2195{
2196    Tcl_ObjCmdProc *proc;
2197
2198    proc = Rappture::GetOpFromObj(interp, nPlaneOps, planeOps,
2199                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2200    if (proc == NULL) {
2201        return TCL_ERROR;
2202    }
2203    return (*proc) (clientData, interp, objc, objv);
2204}
2205
2206#endif  /*PLANE_CMD*/
2207
2208/*
2209 * This command should be Tcl procedure instead of a C command.  The reason
2210 * for this that 1) we are using a safe interpreter so we would need a master
2211 * interpreter to load the Tcl environment properly (including our "unirect2d"
2212 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2213 * easy to add new directories for procedures, since it's loaded into /tmp.
2214 *
2215 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2216 * to verify the structure and then pass it to the appropiate Tcl command
2217 * (heightmap, volume, etc). Our C command always creates a heightmap.
2218 */
2219static int
2220Unirect2dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2221             Tcl_Obj *const *objv)
2222{
2223    Rappture::Unirect2d *dataPtr = (Rappture::Unirect2d *)clientData;
2224
2225    return dataPtr->loadData(interp, objc, objv);
2226}
2227
2228/*
2229 * This command should be Tcl procedure instead of a C command.  The reason
2230 * for this that 1) we are using a safe interpreter so we would need a master
2231 * interpreter to load the Tcl environment properly (including our "unirect2d"
2232 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2233 * easy to add new directories for procedures, since it's loaded into /tmp.
2234 *
2235 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2236 * to verify the structure and then pass it to the appropiate Tcl command
2237 * (heightmap, volume, etc). Our C command always creates a heightmap.
2238 */
2239
2240static int
2241Unirect3dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2242             Tcl_Obj *const *objv)
2243{
2244    Rappture::Unirect3d *dataPtr = (Rappture::Unirect3d *)clientData;
2245
2246    return dataPtr->loadData(interp, objc, objv);
2247}
2248
2249Tcl_Interp *
2250initTcl()
2251{
2252    /*
2253     * Ideally the connection is authenticated by nanoscale.  I still like the
2254     * idea of creating a non-safe master interpreter with a safe slave
2255     * interpreter.  Alias all the nanovis commands in the slave. That way we
2256     * can still run Tcl code within nanovis.  The eventual goal is to create
2257     * a test harness through the interpreter for nanovis.
2258     */
2259    Tcl_Interp *interp;
2260    interp = Tcl_CreateInterp();
2261    /*
2262    Tcl_MakeSafe(interp);
2263    */
2264    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
2265    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
2266    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
2267    if (FlowCmdInitProc(interp) != TCL_OK) {
2268        return NULL;
2269    }
2270    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
2271    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
2272    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
2273#if PLANE_CMD
2274    Tcl_CreateObjCommand(interp, "plane",       PlaneCmd,       NULL, NULL);
2275#endif
2276    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
2277    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    NULL, NULL);
2278    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   NULL, NULL);
2279    Tcl_CreateObjCommand(interp, "unirect2d",   Unirect2dCmd,   NULL, NULL);
2280    Tcl_CreateObjCommand(interp, "unirect3d",   Unirect3dCmd,   NULL, NULL);
2281    Tcl_CreateObjCommand(interp, "up",          UpCmd,          NULL, NULL);
2282    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
2283
2284    Tcl_InitHashTable(&NanoVis::volumeTable, TCL_STRING_KEYS);
2285    Tcl_InitHashTable(&NanoVis::heightmapTable, TCL_STRING_KEYS);
2286    // create a default transfer function
2287    if (Tcl_Eval(interp, def_transfunc) != TCL_OK) {
2288        WARN("bad default transfer function\n%s\n",
2289             Tcl_GetStringResult(interp));
2290    }
2291    return interp;
2292}
2293
2294
Note: See TracBrowser for help on using the repository browser.