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

Last change on this file since 2892 was 2880, checked in by ldelgass, 12 years ago
  1. 2877 included some files that weren't intended to be checked in yet, but

now that the cat is out of the bag, include the rest of the fixes to the dx
reader, gradient computation, etc. old and new dx reader functions are merged
into one now. Camera behavior still defaults to what the old client side
expects.

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