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

Last change on this file since 2929 was 2929, checked in by ldelgass, 13 years ago

fix a couple more (ifdef-ed out) method calls to match rev.2928

  • 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->setScreenSize(0, 0, NanoVis::winWidth, NanoVis::winHeight);
696    NanoVis::cam->setScreenSize(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.