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

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

Remove cruft - ifdef disabled code that calls nonexistant functions

  • Property svn:eol-style set to native
File size: 72.4 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 *        x Convert to Tcl_CmdObj interface. (done)
27 *        o Use Tcl command option parser to reduce size of procedures, remove
28 *          lots of extra error checking code. (almost there)
29 *        o Convert GetVolumeIndices to GetVolumes.  Goal is to remove
30 *          all references of Nanovis::volume[] from this file.  Don't
31 *          want to know how volumes are stored. Same for heightmaps.
32 *        o Rationalize volume id scheme. Right now it's the index in
33 *          the vector. 1) Use a list instead of a vector. 2) carry
34 *          an id field that's a number that gets incremented each new volume.
35 *        x Create R2, matrix, etc. libraries. (done)
36 *        o Add bookkeeping for volumes, heightmaps, flows, etc. to track
37 *          1) id #  2) simulation # 3) include/exclude.  The include/exclude
38 *          is to indicate whether the item should contribute to the overall
39 *          limits of the axes.
40 */
41
42
43
44#include <assert.h>
45#include <stdlib.h>
46#include <tcl.h>
47
48#include <RpField1D.h>
49#include <RpFieldRect3D.h>
50#include <RpFieldPrism3D.h>
51#include <RpEncode.h>
52#include <RpOutcome.h>
53#include <RpBuffer.h>
54
55#include "Trace.h"
56#include "nanovis.h"
57#include "CmdProc.h"
58#include "PointSetRenderer.h"
59#include "PointSet.h"
60#include "ZincBlendeVolume.h"
61#include "NvColorTableRenderer.h"
62#include "NvEventLog.h"
63#include "NvZincBlendeReconstructor.h"
64#include "VolumeInterpolator.h"
65#include "HeightMap.h"
66#include "Grid.h"
67#include "NvCamera.h"
68#include "RenderContext.h"
69#include "NvLIC.h"
70#include "Unirect.h"
71
72#define PLANE_CMD               0
73
74// EXTERN DECLARATIONS
75// in Nv.cpp
76
77// in nanovis.cpp
78extern std::vector<PointSet*> g_pointSet;
79
80extern PlaneRenderer* plane_render;
81extern Texture2D* plane[10];
82
83// Tcl interpreter for incoming messages
84
85// default transfer function
86static const char def_transfunc[] =
87    "transfunc define default {\n\
88  0.0  1 1 1\n\
89  0.2  1 1 0\n\
90  0.4  0 1 0\n\
91  0.6  0 1 1\n\
92  0.8  0 0 1\n\
93  1.0  1 0 1\n\
94} {\n\
95  0.00  1.0\n\
96  0.05  0.0\n\
97  0.15  0.0\n\
98  0.20  1.0\n\
99  0.25  0.0\n\
100  0.35  0.0\n\
101  0.40  1.0\n\
102  0.45  0.0\n\
103  0.55  0.0\n\
104  0.60  1.0\n\
105  0.65  0.0\n\
106  0.75  0.0\n\
107  0.80  1.0\n\
108  0.85  0.0\n\
109  0.95  0.0\n\
110  1.00  1.0\n\
111}";
112
113static Tcl_ObjCmdProc AxisCmd;
114static Tcl_ObjCmdProc CameraCmd;
115static Tcl_ObjCmdProc CutplaneCmd;
116extern Tcl_AppInitProc FlowCmdInitProc;
117static Tcl_ObjCmdProc GridCmd;
118static Tcl_ObjCmdProc LegendCmd;
119#if PLANE_CMD
120static Tcl_ObjCmdProc PlaneCmd;
121#endif
122static Tcl_ObjCmdProc ScreenCmd;
123static Tcl_ObjCmdProc SnapshotCmd;
124static Tcl_ObjCmdProc TransfuncCmd;
125static Tcl_ObjCmdProc Unirect2dCmd;
126static Tcl_ObjCmdProc UpCmd;
127static Tcl_ObjCmdProc VolumeCmd;
128
129bool
130GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
131{
132    int value;
133
134    if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) {
135        return TCL_ERROR;
136    }
137    *boolPtr = (bool)value;
138    return TCL_OK;
139}
140
141int
142GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
143{
144    double value;
145
146    if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
147        return TCL_ERROR;
148    }
149    *valuePtr = (float)value;
150    return TCL_OK;
151}
152
153static int
154GetCullMode(Tcl_Interp *interp, Tcl_Obj *objPtr,
155            graphics::RenderContext::CullMode *modePtr)
156{
157    const char *string = Tcl_GetString(objPtr);
158    if (strcmp(string, "none") == 0) {
159        *modePtr = graphics::RenderContext::NO_CULL;
160    } else if (strcmp(string, "front") == 0) {
161        *modePtr = graphics::RenderContext::FRONT;
162    } else if (strcmp(string, "back") == 0) {
163        *modePtr = graphics::RenderContext::BACK;
164    } else {
165        Tcl_AppendResult(interp, "invalid cull mode \"", string,
166                         "\": should be front, back, or none\"", (char *)NULL);
167        return TCL_ERROR;
168    }
169    return TCL_OK;
170}
171
172static int
173GetShadingModel(Tcl_Interp *interp, Tcl_Obj *objPtr,
174                graphics::RenderContext::ShadingModel *modelPtr)
175{
176    const char *string = Tcl_GetString(objPtr);
177
178    if (strcmp(string,"flat") == 0) {
179        *modelPtr = graphics::RenderContext::FLAT;
180    } else if (strcmp(string,"smooth") == 0) {
181        *modelPtr = graphics::RenderContext::SMOOTH;
182    } else {
183        Tcl_AppendResult(interp, "bad shading model \"", string,
184                         "\": should be flat or smooth", (char *)NULL);
185        return TCL_ERROR;
186    }
187    return TCL_OK;
188}
189
190static int
191GetPolygonMode(Tcl_Interp *interp, Tcl_Obj *objPtr,
192               graphics::RenderContext::PolygonMode *modePtr)
193{
194    const char *string = Tcl_GetString(objPtr);
195
196    if (strcmp(string,"wireframe") == 0) {
197        *modePtr = graphics::RenderContext::LINE;
198    } else if (strcmp(string,"fill") == 0) {
199        *modePtr = graphics::RenderContext::FILL;
200    } else {
201        Tcl_AppendResult(interp, "invalid polygon mode \"", string,
202                         "\": should be wireframe or fill\"", (char *)NULL);
203        return TCL_ERROR;
204    }
205    return TCL_OK;
206}
207
208/*
209 * -----------------------------------------------------------------------
210 *
211 * CreateHeightMap --
212 *
213 *      Creates a heightmap from the given the data. The format of the data
214 *      should be as follows:
215 *
216 *              xMin, xMax, xNum, yMin, yMax, yNum, heights...
217 *
218 *      xNum and yNum must be integer values, all others are real numbers.
219 *      The number of heights must be xNum * yNum;
220 *
221 * -----------------------------------------------------------------------
222 */
223static HeightMap *
224CreateHeightMap(ClientData clientData, Tcl_Interp *interp, int objc,
225                Tcl_Obj *const *objv)
226{
227    float xMin, yMin, xMax, yMax;
228    int xNum, yNum;
229
230    if (objc != 7) {
231        Tcl_AppendResult(interp,
232        "wrong # of values: should be xMin yMin xMax yMax xNum yNum heights",
233        (char *)NULL);
234        return NULL;
235    }
236    if ((GetFloatFromObj(interp, objv[0], &xMin) != TCL_OK) ||
237        (GetFloatFromObj(interp, objv[1], &yMin) != TCL_OK) ||
238        (GetFloatFromObj(interp, objv[2], &xMax) != TCL_OK) ||
239        (GetFloatFromObj(interp, objv[3], &yMax) != TCL_OK) ||
240        (Tcl_GetIntFromObj(interp, objv[4], &xNum) != TCL_OK) ||
241        (Tcl_GetIntFromObj(interp, objv[5], &yNum) != TCL_OK)) {
242        return NULL;
243    }
244    int nValues;
245    Tcl_Obj **elem;
246    if (Tcl_ListObjGetElements(interp, objv[6], &nValues, &elem) != TCL_OK) {
247        return NULL;
248    }
249    if ((xNum <= 0) || (yNum <= 0)) {
250        Tcl_AppendResult(interp, "bad number of x or y values", (char *)NULL);
251        return NULL;
252    }
253    if (nValues != (xNum * yNum)) {
254        Tcl_AppendResult(interp, "wrong # of heights", (char *)NULL);
255        return NULL;
256    }
257
258    float *heights;
259    heights = new float[nValues];
260    if (heights == NULL) {
261        Tcl_AppendResult(interp, "can't allocate array of heights",
262                         (char *)NULL);
263        return NULL;
264    }
265
266    int i;
267    for (i = 0; i < nValues; i++) {
268        if (GetFloatFromObj(interp, elem[i], heights + i) != TCL_OK) {
269            delete [] heights;
270            return NULL;
271        }
272    }
273    HeightMap* hmPtr;
274    hmPtr = new HeightMap();
275    hmPtr->setHeight(xMin, yMin, xMax, yMax, xNum, yNum, heights);
276    hmPtr->transferFunction(NanoVis::get_transfunc("default"));
277    hmPtr->setVisible(true);
278    hmPtr->setLineContourVisible(true);
279    delete [] heights;
280    return hmPtr;
281}
282
283/*
284 * ----------------------------------------------------------------------
285 *
286 * GetHeightMapFromObj --
287 *
288 * ----------------------------------------------------------------------
289 */
290static int
291GetHeightMapFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, HeightMap **hmPtrPtr)
292{
293    const char *string;
294    string = Tcl_GetString(objPtr);
295
296    Tcl_HashEntry *hPtr;
297    hPtr = Tcl_FindHashEntry(&NanoVis::heightmapTable, string);
298    if (hPtr == NULL) {
299        if (interp != NULL) {
300            Tcl_AppendResult(interp, "can't find a heightmap named \"",
301                         string, "\"", (char*)NULL);
302        }
303        return TCL_ERROR;
304    }
305    *hmPtrPtr = (HeightMap *)Tcl_GetHashValue(hPtr);
306    return TCL_OK;
307}
308
309
310/*
311 * ----------------------------------------------------------------------
312 * FUNCTION: GetVolumeFromObj
313 *
314 * Used internally to decode a series of volume index values and
315 * store then in the specified vector.  If there are no volume index
316 * arguments, this means "all volumes" to most commands, so all
317 * active volume indices are stored in the vector.
318 *
319 * Updates pushes index values into the vector.  Returns TCL_OK or
320 * TCL_ERROR to indicate an error.
321 * ----------------------------------------------------------------------
322 */
323static int
324GetVolumeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Volume **volPtrPtr)
325{
326    const char *string;
327    string = Tcl_GetString(objPtr);
328
329    Tcl_HashEntry *hPtr;
330    hPtr = Tcl_FindHashEntry(&NanoVis::volumeTable, string);
331    if (hPtr == NULL) {
332        if (interp != NULL) {
333            Tcl_AppendResult(interp, "can't find a volume named \"",
334                         string, "\"", (char*)NULL);
335        }
336        return TCL_ERROR;
337    }
338    *volPtrPtr = (Volume *)Tcl_GetHashValue(hPtr);
339    return TCL_OK;
340}
341
342/*
343 * ----------------------------------------------------------------------
344 * FUNCTION: GetVolumes()
345 *
346 * Used internally to decode a series of volume index values and
347 * store then in the specified vector.  If there are no volume index
348 * arguments, this means "all volumes" to most commands, so all
349 * active volume indices are stored in the vector.
350 *
351 * Updates pushes index values into the vector.  Returns TCL_OK or
352 * TCL_ERROR to indicate an error.
353 * ----------------------------------------------------------------------
354 */
355static int
356GetVolumes(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
357           std::vector<Volume *>* vectorPtr)
358{
359    if (objc == 0) {
360        // No arguments. Get all volumes.
361        Tcl_HashSearch iter;
362        Tcl_HashEntry *hPtr;
363        for (hPtr = Tcl_FirstHashEntry(&NanoVis::volumeTable, &iter);
364             hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) {
365            Volume *volPtr;
366            volPtr = (Volume *)Tcl_GetHashValue(hPtr);
367            vectorPtr->push_back(volPtr);
368        }
369    } else {
370        // Get the volumes associated with the given index arguments.
371        for (int n = 0; n < objc; n++) {
372            Volume *volPtr;
373
374            if (GetVolumeFromObj(interp, objv[n], &volPtr) != TCL_OK) {
375                return TCL_ERROR;
376            }
377            vectorPtr->push_back(volPtr);
378        }
379    }
380    return TCL_OK;
381}
382
383/*
384 * ----------------------------------------------------------------------
385 * FUNCTION: GetHeightMaps()
386 *
387 * Used internally to decode a series of volume index values and
388 * store then in the specified vector.  If there are no volume index
389 * arguments, this means "all volumes" to most commands, so all
390 * active volume indices are stored in the vector.
391 *
392 * Updates pushes index values into the vector.  Returns TCL_OK or
393 * TCL_ERROR to indicate an error.
394 * ----------------------------------------------------------------------
395 */
396static int
397GetHeightMaps(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv,
398              std::vector<HeightMap *>* vectorPtr)
399{
400    if (objc == 0) {
401        // No arguments. Get all heightmaps.
402        Tcl_HashSearch iter;
403        Tcl_HashEntry *hPtr;
404        for (hPtr = Tcl_FirstHashEntry(&NanoVis::heightmapTable, &iter);
405             hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) {
406            HeightMap *hmPtr;
407            hmPtr = (HeightMap *)Tcl_GetHashValue(hPtr);
408            vectorPtr->push_back(hmPtr);
409        }
410    } else {
411        for (int n = 0; n < objc; n++) {
412            HeightMap *hmPtr;
413
414            if (GetHeightMapFromObj(interp, objv[n], &hmPtr) != TCL_OK) {
415                return TCL_ERROR;
416            }
417            vectorPtr->push_back(hmPtr);
418        }
419    }
420    return TCL_OK;
421}
422
423
424/*
425 * ----------------------------------------------------------------------
426 * FUNCTION: GetAxis()
427 *
428 * Used internally to decode an axis value from a string ("x", "y",
429 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
430 * along with a value in valPtr.  Otherwise, it returns TCL_ERROR
431 * and an error message in the interpreter.
432 * ----------------------------------------------------------------------
433 */
434static int
435GetAxis(Tcl_Interp *interp, const char *string, int *indexPtr)
436{
437    if (string[1] == '\0') {
438        char c;
439
440        c = tolower((unsigned char)string[0]);
441        if (c == 'x') {
442            *indexPtr = 0;
443            return TCL_OK;
444        } else if (c == 'y') {
445            *indexPtr = 1;
446            return TCL_OK;
447        } else if (c == 'z') {
448            *indexPtr = 2;
449            return TCL_OK;
450        }
451        /*FALLTHRU*/
452    }
453    Tcl_AppendResult(interp, "bad axis \"", string,
454                     "\": should be x, y, or z", (char*)NULL);
455    return TCL_ERROR;
456}
457
458/*
459 * ----------------------------------------------------------------------
460 * FUNCTION: GetAxisFromObj()
461 *
462 * Used internally to decode an axis value from a string ("x", "y",
463 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
464 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
465 * and an error message in the interpreter.
466 * ----------------------------------------------------------------------
467 */
468int
469GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr)
470{
471    return GetAxis(interp, Tcl_GetString(objPtr), indexPtr);
472}
473
474/*
475 * ----------------------------------------------------------------------
476 * FUNCTION: GetAxisDirFromObj()
477 *
478 * Used internally to decode an axis value from a string ("x", "y",
479 * or "z") to its index (0, 1, or 2).  Returns TCL_OK if successful,
480 * along with a value in indexPtr.  Otherwise, it returns TCL_ERROR
481 * and an error message in the interpreter.
482 * ----------------------------------------------------------------------
483 */
484static int
485GetAxisDirFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr, int *dirPtr)
486{
487    const char *string = Tcl_GetString(objPtr);
488
489    int sign = 1;
490    if (*string == '-') {
491        sign = -1;
492        string++;
493    }
494    if (GetAxis(interp, string, indexPtr) != TCL_OK) {
495        return TCL_ERROR;
496    }
497    if (dirPtr != NULL) {
498        *dirPtr = sign;
499    }
500    return TCL_OK;
501}
502
503/*
504 * ----------------------------------------------------------------------
505 * FUNCTION: GetColor()
506 *
507 * Used internally to decode a color value from a string ("R G B")
508 * as a list of three numbers 0-1.  Returns TCL_OK if successful,
509 * along with RGB values in rgbPtr.  Otherwise, it returns TCL_ERROR
510 * and an error message in the interpreter.
511 * ----------------------------------------------------------------------
512 */
513static int
514GetColor(Tcl_Interp *interp, int objc, Tcl_Obj *const *objv, float *rgbPtr)
515{
516    if (objc < 3) {
517        Tcl_AppendResult(interp, "missing color values\": ",
518                         "should list of R G B values 0.0 - 1.0", (char*)NULL);
519        return TCL_ERROR;
520    }
521    if ((GetFloatFromObj(interp, objv[0], rgbPtr + 0) != TCL_OK) ||
522        (GetFloatFromObj(interp, objv[1], rgbPtr + 1) != TCL_OK) ||
523        (GetFloatFromObj(interp, objv[2], rgbPtr + 2) != TCL_OK)) {
524        return TCL_ERROR;
525    }
526    return TCL_OK;
527}
528
529
530/*
531 * -----------------------------------------------------------------------
532 *
533 * GetDataStream --
534 *
535 *      Read the requested number of bytes from standard input into the given
536 *      buffer.  The buffer is then decompressed and decoded.
537 *
538 * -----------------------------------------------------------------------
539 */
540int
541GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes)
542{
543    char buffer[8096];
544
545    clearerr(NanoVis::stdin);
546    while (nBytes > 0) {
547        unsigned int chunk;
548        int nRead;
549
550        chunk = (sizeof(buffer) < (unsigned int) nBytes) ?
551            sizeof(buffer) : nBytes;
552        nRead = fread(buffer, sizeof(char), chunk, NanoVis::stdin);
553        if (ferror(NanoVis::stdin)) {
554            Tcl_AppendResult(interp, "while reading data stream: ",
555                             Tcl_PosixError(interp), (char*)NULL);
556            return TCL_ERROR;
557        }
558        if (feof(NanoVis::stdin)) {
559            Tcl_AppendResult(interp, "premature EOF while reading data stream",
560                             (char*)NULL);
561            return TCL_ERROR;
562        }
563        buf.append(buffer, nRead);
564        nBytes -= nRead;
565    }
566    if (NanoVis::recfile != NULL) {
567        ssize_t nWritten;
568
569        nWritten = fwrite(buf.bytes(), sizeof(char), buf.size(),
570                          NanoVis::recfile);
571        assert(nWritten == (ssize_t)buf.size());
572        fflush(NanoVis::recfile);
573    }
574    Rappture::Outcome err;
575    TRACE("Checking header[%.13s]\n", buf.bytes());
576    if (strncmp (buf.bytes(), "@@RP-ENC:", 9) == 0) {
577        /* There's a header on the buffer, use it to decode the data. */
578        if (!Rappture::encoding::decode(err, buf, RPENC_HDR)) {
579            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
580            return TCL_ERROR;
581        }
582    } else if (Rappture::encoding::isBase64(buf.bytes(), buf.size())) {
583        /* No header, but it's base64 encoded.  It's likely that it's both
584         * base64 encoded and compressed. */
585        if (!Rappture::encoding::decode(err, buf, RPENC_B64 | RPENC_Z)) {
586            Tcl_AppendResult(interp, err.remark(), (char*)NULL);
587            return TCL_ERROR;
588        }
589    }
590    return TCL_OK;
591}
592
593
594static int
595CameraAimOp(ClientData clientData, Tcl_Interp *interp, int objc,
596            Tcl_Obj *const *objv)
597{
598    float x, y, z;
599    if ((GetFloatFromObj(interp, objv[2], &x) != TCL_OK) ||
600        (GetFloatFromObj(interp, objv[3], &y) != TCL_OK) ||
601        (GetFloatFromObj(interp, objv[4], &z) != TCL_OK)) {
602        return TCL_ERROR;
603    }
604    NanoVis::cam->xAim(x);
605    NanoVis::cam->yAim(y);
606    NanoVis::cam->zAim(z);
607    return TCL_OK;
608}
609
610static int
611CameraAngleOp(ClientData clientData, Tcl_Interp *interp, int objc,
612              Tcl_Obj *const *objv)
613{
614    float theta, phi, psi;
615    if ((GetFloatFromObj(interp, objv[2], &phi) != TCL_OK) ||
616        (GetFloatFromObj(interp, objv[3], &theta) != TCL_OK) ||
617        (GetFloatFromObj(interp, objv[4], &psi) != TCL_OK)) {
618        return TCL_ERROR;
619    }
620    NanoVis::cam->rotate(phi, theta, psi);
621    return TCL_OK;
622}
623
624
625static int
626CameraPanOp(ClientData clientData, Tcl_Interp *interp, int objc,
627             Tcl_Obj *const *objv)
628{
629    float x, y;
630    if ((GetFloatFromObj(interp, objv[2], &x) != TCL_OK) ||
631        (GetFloatFromObj(interp, objv[3], &y) != TCL_OK)) {
632        return TCL_ERROR;
633    }
634    NanoVis::pan(x, y);
635    return TCL_OK;
636}
637
638static int
639CameraZoomOp(ClientData clientData, Tcl_Interp *interp, int objc,
640             Tcl_Obj *const *objv)
641{
642    float zoom;
643    if (GetFloatFromObj(interp, objv[2], &zoom) != TCL_OK) {
644        return TCL_ERROR;
645    }
646    NanoVis::cam->z(-2.5f / zoom);
647    return TCL_OK;
648}
649
650static Rappture::CmdSpec cameraOps[] = {
651    {"aim",     2, CameraAimOp,      5, 5, "x y z",},
652    {"angle",   2, CameraAngleOp,    5, 5, "xAngle yAngle zAngle",},
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::win_width, h = NanoVis::win_height;
693    NanoVis::resize_offscreen_buffer(2048, 2048);
694#ifdef notdef
695    NanoVis::cam->set_screen_size(0, 0, NanoVis::win_width,NanoVis::win_height);
696    NanoVis::cam->set_screen_size(30, 90, 2048 - 60, 2048 - 120);
697#endif
698    NanoVis::offscreen_buffer_capture();  //enable offscreen render
699    NanoVis::display();
700    NanoVis::read_screen();
701#ifdef notdef
702    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
703#endif
704    NanoVis::ppm_write("nv>image -bytes %d -type print");
705    NanoVis::resize_offscreen_buffer(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)->move_cutplane(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)->enable_cutplane(axis);
766        }
767    } else {
768        std::vector<Volume *>::iterator iter;
769        for (iter = ivol.begin(); iter != ivol.end(); iter++) {
770            (*iter)->disable_cutplane(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::get_transfunc(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::update_pending) {
847        NanoVis::SetVolumeRanges();
848    }
849    NanoVis::render_legend(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::resize_offscreen_buffer(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::vol_renderer->getVolumeInterpolator();
1028    interpolator->start();
1029    if (interpolator->is_started()) {
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::offscreen_buffer_capture();  //enable offscreen render
1040
1041            NanoVis::display();
1042            NanoVis::read_screen();
1043
1044            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
1045
1046            NanoVis::bmp_write_to_file(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::vol_renderer->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::vol_renderer->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::vol_renderer->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::vol_renderer->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#if ISO_TEST
1190        volPtr = load_volume_stream2(context, tag, fdata);
1191#else
1192        volPtr = load_volume_stream(context, tag, fdata);
1193#endif
1194        if (volPtr == NULL) {
1195            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
1196            return TCL_ERROR;
1197        }
1198    }
1199
1200    //
1201    // BE CAREFUL: Set the number of slices to something slightly different
1202    // for each volume.  If we have identical volumes at exactly the same
1203    // position with exactly the same number of slices, the second volume will
1204    // overwrite the first, so the first won't appear at all.
1205    //
1206    if (volPtr != NULL) {
1207        //volPtr->n_slices(512-n);
1208        //volPtr->n_slices(256-n);
1209        volPtr->disable_cutplane(0);
1210        volPtr->disable_cutplane(1);
1211        volPtr->disable_cutplane(2);
1212        volPtr->transferFunction(NanoVis::get_transfunc("default"));
1213        volPtr->visible(true);
1214
1215        char info[1024];
1216        ssize_t nWritten;
1217
1218        if (Volume::update_pending) {
1219            NanoVis::SetVolumeRanges();
1220        }
1221
1222        // FIXME: strlen(info) is the return value of sprintf
1223        sprintf(info, "nv>data tag %s min %g max %g vmin %g vmax %g\n", tag,
1224                volPtr->wAxis.min(), volPtr->wAxis.max(),
1225                Volume::valueMin, Volume::valueMax);
1226        nWritten  = write(1, info, strlen(info));
1227        assert(nWritten == (ssize_t)strlen(info));
1228    }
1229    return TCL_OK;
1230}
1231
1232static int
1233VolumeDataStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1234                  Tcl_Obj *const *objv)
1235{
1236    bool state;
1237    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1238        return TCL_ERROR;
1239    }
1240    std::vector<Volume *> ivol;
1241    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1242        return TCL_ERROR;
1243    }
1244    std::vector<Volume *>::iterator iter;
1245    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1246        (*iter)->data_enabled(state);
1247    }
1248    return TCL_OK;
1249}
1250
1251static Rappture::CmdSpec volumeDataOps[] = {
1252    {"follows",   1, VolumeDataFollowsOp, 5, 5, "size tag",},
1253    {"state",     1, VolumeDataStateOp,   4, 0, "bool ?indices?",},
1254};
1255static int nVolumeDataOps = NumCmdSpecs(volumeDataOps);
1256
1257static int
1258VolumeDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
1259             Tcl_Obj *const *objv)
1260{
1261    Tcl_ObjCmdProc *proc;
1262
1263    proc = Rappture::GetOpFromObj(interp, nVolumeDataOps, volumeDataOps,
1264                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1265    if (proc == NULL) {
1266        return TCL_ERROR;
1267    }
1268    return (*proc) (clientData, interp, objc, objv);
1269}
1270
1271/*
1272 *---------------------------------------------------------------------------
1273 *
1274 * VolumeDeleteOp --
1275 *
1276 *---------------------------------------------------------------------------
1277 */
1278/*ARGSUSED*/
1279static int
1280VolumeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1281               Tcl_Obj *const *objv)
1282{
1283    int i;
1284
1285    for (i = 2; i < objc; i++) {
1286        Volume *volPtr;
1287
1288        if (GetVolumeFromObj(interp, objv[i], &volPtr) != TCL_OK) {
1289            return TCL_ERROR;
1290        }
1291        NanoVis::remove_volume(volPtr);
1292    }
1293    NanoVis::EventuallyRedraw();
1294    return TCL_OK;
1295}
1296
1297/*
1298 *---------------------------------------------------------------------------
1299 *
1300 * VolumeExistsOp --
1301 *
1302 *---------------------------------------------------------------------------
1303 */
1304/*ARGSUSED*/
1305static int
1306VolumeExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1307               Tcl_Obj *const *objv)
1308{
1309    bool value;
1310    Volume *volPtr;
1311
1312    value = false;
1313    if (GetVolumeFromObj(NULL, objv[2], &volPtr) == TCL_OK) {
1314        value = true;
1315    }
1316    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
1317    return TCL_OK;
1318}
1319
1320/*
1321 *---------------------------------------------------------------------------
1322 *
1323 * VolumeNamesOp --
1324 *
1325 *---------------------------------------------------------------------------
1326 */
1327/*ARGSUSED*/
1328static int
1329VolumeNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1330              Tcl_Obj *const *objv)
1331{
1332    Tcl_Obj *listObjPtr;
1333    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
1334    Tcl_HashEntry *hPtr;
1335    Tcl_HashSearch iter;
1336    for (hPtr = Tcl_FirstHashEntry(&NanoVis::volumeTable, &iter); hPtr != NULL;
1337         hPtr = Tcl_NextHashEntry(&iter)) {
1338        Volume *volPtr;
1339        volPtr = (Volume *)Tcl_GetHashValue(hPtr);
1340        Tcl_Obj *objPtr;
1341        objPtr = Tcl_NewStringObj(volPtr->name(), -1);
1342        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
1343    }
1344    Tcl_SetObjResult(interp, listObjPtr);
1345    return TCL_OK;
1346}
1347
1348static int
1349VolumeOutlineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1350                     Tcl_Obj *const *objv)
1351{
1352    float rgb[3];
1353    if (GetColor(interp, objc - 3, objv + 3, rgb) != TCL_OK) {
1354        return TCL_ERROR;
1355    }
1356    std::vector<Volume *> ivol;
1357    if (GetVolumes(interp, objc - 6, objv + 6, &ivol) != TCL_OK) {
1358        return TCL_ERROR;
1359    }
1360    std::vector<Volume *>::iterator iter;
1361    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1362        (*iter)->set_outline_color(rgb);
1363    }
1364    return TCL_OK;
1365}
1366
1367static int
1368VolumeOutlineStateOp(ClientData clientData, Tcl_Interp *interp, int objc,
1369                     Tcl_Obj *const *objv)
1370{
1371    bool state;
1372    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
1373        return TCL_ERROR;
1374    }
1375    std::vector<Volume *> ivol;
1376    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1377        return TCL_ERROR;
1378    }
1379    std::vector<Volume *>::iterator iter;
1380    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1381        (*iter)->outline(state);
1382    }
1383    return TCL_OK;
1384}
1385
1386
1387static Rappture::CmdSpec volumeOutlineOps[] = {
1388    {"color",     1, VolumeOutlineColorOp,  6, 0, "r g b ?indices?",},
1389    {"state",     1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1390    {"visible",   1, VolumeOutlineStateOp,  4, 0, "bool ?indices?",},
1391};
1392static int nVolumeOutlineOps = NumCmdSpecs(volumeOutlineOps);
1393
1394static int
1395VolumeOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc,
1396                Tcl_Obj *const *objv)
1397{
1398    Tcl_ObjCmdProc *proc;
1399
1400    proc = Rappture::GetOpFromObj(interp, nVolumeOutlineOps, volumeOutlineOps,
1401        Rappture::CMDSPEC_ARG2, objc, objv, 0);
1402    if (proc == NULL) {
1403        return TCL_ERROR;
1404    }
1405    return (*proc) (clientData, interp, objc, objv);
1406}
1407
1408static int
1409VolumeShadingDiffuseOp(ClientData clientData, Tcl_Interp *interp, int objc,
1410                       Tcl_Obj *const *objv)
1411{
1412    float diffuse;
1413    if (GetFloatFromObj(interp, objv[3], &diffuse) != TCL_OK) {
1414        return TCL_ERROR;
1415    }
1416
1417    std::vector<Volume *> ivol;
1418    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1419        return TCL_ERROR;
1420    }
1421    std::vector<Volume *>::iterator iter;
1422    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1423        (*iter)->diffuse(diffuse);
1424    }
1425    return TCL_OK;
1426}
1427
1428static int
1429VolumeShadingIsosurfaceOp(ClientData clientData, Tcl_Interp *interp, int objc,
1430                          Tcl_Obj *const *objv)
1431{
1432    bool iso_surface;
1433    if (GetBooleanFromObj(interp, objv[3], &iso_surface) != TCL_OK) {
1434        return TCL_ERROR;
1435    }
1436    std::vector<Volume *> ivol;
1437    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1438        return TCL_ERROR;
1439    }
1440    std::vector<Volume *>::iterator iter;
1441    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1442        (*iter)->isosurface(iso_surface);
1443    }
1444    return TCL_OK;
1445}
1446
1447static int
1448VolumeShadingOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1449                       Tcl_Obj *const *objv)
1450{
1451
1452    float opacity;
1453    if (GetFloatFromObj(interp, objv[3], &opacity) != TCL_OK) {
1454        return TCL_ERROR;
1455    }
1456    TRACE("set opacity %f\n", opacity);
1457    std::vector<Volume *> ivol;
1458    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1459        return TCL_ERROR;
1460    }
1461    std::vector<Volume *>::iterator iter;
1462    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1463        (*iter)->opacity_scale(opacity);
1464    }
1465    return TCL_OK;
1466}
1467
1468static int
1469VolumeShadingSpecularOp(ClientData clientData, Tcl_Interp *interp, int objc,
1470                        Tcl_Obj *const *objv)
1471{
1472    float specular;
1473    if (GetFloatFromObj(interp, objv[3], &specular) != TCL_OK) {
1474        return TCL_ERROR;
1475    }
1476    std::vector<Volume *> ivol;
1477    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1478        return TCL_ERROR;
1479    }
1480    std::vector<Volume *>::iterator iter;
1481    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1482        (*iter)->specular(specular);
1483    }
1484    return TCL_OK;
1485}
1486
1487static int
1488VolumeShadingTransFuncOp(ClientData clientData, Tcl_Interp *interp, int objc,
1489                         Tcl_Obj *const *objv)
1490{
1491    TransferFunction *tfPtr;
1492    const char *name = Tcl_GetString(objv[3]);
1493    tfPtr = NanoVis::get_transfunc(name);
1494    if (tfPtr == NULL) {
1495        Tcl_AppendResult(interp, "transfer function \"", name,
1496                         "\" is not defined", (char*)NULL);
1497        return TCL_ERROR;
1498    }
1499    std::vector<Volume *> ivol;
1500    if (GetVolumes(interp, objc - 4, objv + 4, &ivol) != TCL_OK) {
1501        return TCL_ERROR;
1502    }
1503    std::vector<Volume *>::iterator iter;
1504    for (iter = ivol.begin(); iter != ivol.end(); iter++) {
1505        TRACE("setting %s with transfer function %s\n", (*iter)->name(),
1506               tfPtr->name());
1507        (*iter)->transferFunction(tfPtr);
1508#ifdef POINTSET
1509        // TBD..
1510        // POINTSET
1511        if ((*iter)->pointsetIndex != -1) {
1512            g_pointSet[(*iter)->pointsetIndex]->updateColor(tf->getData(), 256);
1513        }
1514#endif /*POINTSET*/
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)->set_label(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->data_enabled(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::get_transfunc("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::update_pending) {
1869        NanoVis::SetHeightmapRanges();
1870    }
1871    NanoVis::render_legend(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::render_2d_contour(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::get_transfunc("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::render_2d_contour(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::get_transfunc(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,
2016     "tag xmin ymin xmax ymax xnum ynum values",},
2017    {"cull",         2, HeightMapCullOp,        3, 3, "mode",},
2018    {"data",         1, HeightMapDataOp,        3, 0, "oper ?args?",},
2019    {"legend",       2, HeightMapLegendOp,      5, 5, "index width height",},
2020    {"linecontour",  2, HeightMapLineContourOp, 2, 0, "oper ?args?",},
2021    {"opacity",      1, HeightMapOpacityOp,     3, 0, "value ?heightmap...? ",},
2022    {"polygon",      1, HeightMapPolygonOp,     3, 3, "mode",},
2023    {"shading",      1, HeightMapShadingOp,     3, 3, "model",},
2024    {"test",         2, HeightMapTestOp,        2, 2, "",},
2025    {"transfunc",    2, HeightMapTransFuncOp,   3, 0, "name ?heightmap...?",},
2026
2027    // HELP ME
2028    // GOERGE
2029    {"topview",      2, HeightMapTopView,     2, 2, "",},
2030};
2031static int nHeightMapOps = NumCmdSpecs(heightMapOps);
2032
2033static int
2034HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2035             Tcl_Obj *const *objv)
2036{
2037    Tcl_ObjCmdProc *proc;
2038
2039    proc = Rappture::GetOpFromObj(interp, nHeightMapOps, heightMapOps,
2040                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2041    if (proc == NULL) {
2042        return TCL_ERROR;
2043    }
2044    return (*proc) (clientData, interp, objc, objv);
2045}
2046
2047static int
2048GridAxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2049                Tcl_Obj *const *objv)
2050{
2051    float r, g, b, a;
2052    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2053        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2054        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2055        return TCL_ERROR;
2056    }
2057    a = 1.0f;
2058    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2059        return TCL_ERROR;
2060    }
2061    if (NanoVis::grid) {
2062        NanoVis::grid->setAxisColor(r, g, b, a);
2063    }
2064    return TCL_OK;
2065}
2066
2067static int
2068GridAxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc,
2069               Tcl_Obj *const *objv)
2070{
2071    int axis;
2072    if (GetAxisFromObj(interp, objv[2], &axis) != TCL_OK) {
2073        return TCL_ERROR;
2074    }
2075    if (NanoVis::grid != NULL) {
2076        Axis *axisPtr;
2077
2078        axisPtr = NULL;     /* Suppress compiler warning. */
2079        switch (axis) {
2080        case 0: axisPtr = &NanoVis::grid->xAxis; break;
2081        case 1: axisPtr = &NanoVis::grid->yAxis; break;
2082        case 2: axisPtr = &NanoVis::grid->zAxis; break;
2083        }
2084        axisPtr->name(Tcl_GetString(objv[3]));
2085        axisPtr->units(Tcl_GetString(objv[4]));
2086    }
2087    return TCL_OK;
2088}
2089
2090static int
2091GridLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2092                Tcl_Obj *const *objv)
2093{
2094    float r, g, b, a;
2095    if ((GetFloatFromObj(interp, objv[2], &r) != TCL_OK) ||
2096        (GetFloatFromObj(interp, objv[3], &g) != TCL_OK) ||
2097        (GetFloatFromObj(interp, objv[4], &b) != TCL_OK)) {
2098        return TCL_ERROR;
2099    }
2100    a = 1.0f;
2101    if ((objc == 6) && (GetFloatFromObj(interp, objv[5], &a) != TCL_OK)) {
2102        return TCL_ERROR;
2103    }
2104    if (NanoVis::grid) {
2105        NanoVis::grid->setLineColor(r, g, b, a);
2106    }
2107    return TCL_OK;
2108}
2109
2110static int
2111GridVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2112              Tcl_Obj *const *objv)
2113{
2114    bool visible;
2115    if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2116        return TCL_ERROR;
2117    }
2118    NanoVis::grid->setVisible(visible);
2119    return TCL_OK;
2120}
2121
2122static Rappture::CmdSpec gridOps[] = {
2123    {"axiscolor",  5, GridAxisColorOp,  5, 6, "r g b ?a?",},
2124    {"axisname",   5, GridAxisNameOp,   5, 5, "index title units",},
2125    {"linecolor",  7, GridLineColorOp,  5, 6, "r g b ?a?",},
2126    {"visible",    1, GridVisibleOp,    3, 3, "bool",},
2127};
2128static int nGridOps = NumCmdSpecs(gridOps);
2129
2130static int
2131GridCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2132        Tcl_Obj *const *objv)
2133{
2134    Tcl_ObjCmdProc *proc;
2135
2136    proc = Rappture::GetOpFromObj(interp, nGridOps, gridOps,
2137        Rappture::CMDSPEC_ARG1, objc, objv, 0);
2138    if (proc == NULL) {
2139        return TCL_ERROR;
2140    }
2141    return (*proc) (clientData, interp, objc, objv);
2142}
2143
2144static int
2145AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2146        Tcl_Obj *const *objv)
2147{
2148    if (objc < 2) {
2149        Tcl_AppendResult(interp, "wrong # args: should be \"",
2150                Tcl_GetString(objv[0]), " option arg arg...\"", (char*)NULL);
2151        return TCL_ERROR;
2152    }
2153    const char *string = Tcl_GetString(objv[1]);
2154    char c = string[0];
2155    if ((c == 'v') && (strcmp(string, "visible") == 0)) {
2156        bool visible;
2157
2158        if (GetBooleanFromObj(interp, objv[2], &visible) != TCL_OK) {
2159            return TCL_ERROR;
2160        }
2161        NanoVis::axis_on = visible;
2162    } else {
2163        Tcl_AppendResult(interp, "bad axis option \"", string,
2164                         "\": should be visible", (char*)NULL);
2165        return TCL_ERROR;
2166    }
2167    return TCL_OK;
2168}
2169
2170#if PLANE_CMD
2171static int
2172PlaneNewOp(ClientData clientData, Tcl_Interp *interp, int objc,
2173           Tcl_Obj *const *objv)
2174{
2175    TRACE("load plane for 2D visualization command\n");
2176
2177    int index, w, h;
2178    if (objc != 4) {
2179        Tcl_AppendResult(interp, "wrong # args: should be \"",
2180            Tcl_GetString(objv[0]), " plane_index w h \"", (char*)NULL);
2181        return TCL_ERROR;
2182    }
2183    if (Tcl_GetIntFromObj(interp, objv[1], &index) != TCL_OK) {
2184        return TCL_ERROR;
2185    }
2186    if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) {
2187        return TCL_ERROR;
2188    }
2189    if (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) {
2190        return TCL_ERROR;
2191    }
2192
2193    //Now read w*h*4 bytes. The server expects the plane to be a stream of
2194    //floats
2195    char* tmp = new char[int(w*h*sizeof(float))];
2196    if (tmp == NULL) {
2197        Tcl_AppendResult(interp, "can't allocate stream data", (char *)NULL);
2198        return TCL_ERROR;
2199    }
2200    bzero(tmp, w*h*4);
2201    int status = read(0, tmp, w*h*sizeof(float));
2202    if (status <= 0) {
2203        exit(0);                // Bail out on read error?  Should log the
2204                                // error and return a non-zero exit status.
2205    }
2206    plane[index] = new Texture2D(w, h, GL_FLOAT, GL_LINEAR, 1, (float*)tmp);
2207    delete[] tmp;
2208    return TCL_OK;
2209}
2210
2211
2212static int
2213PlaneLinkOp(ClientData clientData, Tcl_Interp *interp, int objc,
2214            Tcl_Obj *const *objv)
2215{
2216    TRACE("link the plane to the 2D renderer command\n");
2217
2218    int plane_index, tf_index;
2219
2220    if (objc != 3) {
2221        Tcl_AppendResult(interp, "wrong # args: should be \"",
2222            Tcl_GetString(objv[0]), " plane_index tf_index \"", (char*)NULL);
2223        return TCL_ERROR;
2224    }
2225    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2226        return TCL_ERROR;
2227    }
2228    if (Tcl_GetIntFromObj(interp, objv[2], &tf_index) != TCL_OK) {
2229        return TCL_ERROR;
2230    }
2231    //plane_render->add_plane(plane[plane_index], tf[tf_index]);
2232    return TCL_OK;
2233}
2234
2235//Enable a 2D plane for render
2236//The plane_index is the index mantained in the 2D plane renderer
2237static int
2238PlaneEnableOp(ClientData clientData, Tcl_Interp *interp, int objc,
2239              Tcl_Obj *const *objv)
2240{
2241    TRACE("enable a plane so the 2D renderer can render it command\n");
2242
2243    if (objc != 3) {
2244        Tcl_AppendResult(interp, "wrong # args: should be \"",
2245            Tcl_GetString(objv[0]), " plane_index mode \"", (char*)NULL);
2246        return TCL_ERROR;
2247    }
2248    int plane_index;
2249    if (Tcl_GetIntFromObj(interp, objv[1], &plane_index) != TCL_OK) {
2250        return TCL_ERROR;
2251    }
2252    int mode;
2253    if (Tcl_GetIntFromObj(interp, objv[2], &mode) != TCL_OK) {
2254        return TCL_ERROR;
2255    }
2256    if (mode == 0) {
2257        plane_index = -1;
2258    }
2259    plane_render->set_active_plane(plane_index);
2260    return TCL_OK;
2261}
2262
2263static Rappture::CmdSpec planeOps[] = {
2264    {"enable",     1, PlaneEnableOp,    4, 4, "planeIdx mode",},
2265    {"link",       1, PlaneLinkOp,      4, 4, "planeIdx transfuncIdx",},
2266    {"new",        1, PlaneNewOp,       5, 5, "planeIdx width height",},
2267};
2268static int nPlaneOps = NumCmdSpecs(planeOps);
2269
2270static int
2271PlaneCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2272         Tcl_Obj *const *objv)
2273{
2274    Tcl_ObjCmdProc *proc;
2275
2276    proc = Rappture::GetOpFromObj(interp, nPlaneOps, planeOps,
2277                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2278    if (proc == NULL) {
2279        return TCL_ERROR;
2280    }
2281    return (*proc) (clientData, interp, objc, objv);
2282}
2283
2284#endif  /*PLANE_CMD*/
2285
2286/*
2287 * This command should be Tcl procedure instead of a C command.  The reason
2288 * for this that 1) we are using a safe interpreter so we would need a master
2289 * interpreter to load the Tcl environment properly (including our "unirect2d"
2290 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2291 * easy to add new directories for procedures, since it's loaded into /tmp.
2292 *
2293 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2294 * to verify the structure and then pass it to the appropiate Tcl command
2295 * (heightmap, volume, etc). Our C command always creates a heightmap.
2296 */
2297static int
2298Unirect2dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2299             Tcl_Obj *const *objv)
2300{
2301    Rappture::Unirect2d *dataPtr = (Rappture::Unirect2d *)clientData;
2302
2303    return dataPtr->LoadData(interp, objc, objv);
2304}
2305
2306/*
2307 * This command should be Tcl procedure instead of a C command.  The reason
2308 * for this that 1) we are using a safe interpreter so we would need a master
2309 * interpreter to load the Tcl environment properly (including our "unirect2d"
2310 * procedure). And 2) the way nanovis is currently deployed doesn't make it
2311 * easy to add new directories for procedures, since it's loaded into /tmp.
2312 *
2313 * Ideally, the "unirect2d" proc would do a rundimentary parsing of the data
2314 * to verify the structure and then pass it to the appropiate Tcl command
2315 * (heightmap, volume, etc). Our C command always creates a heightmap.
2316 */
2317
2318static int
2319Unirect3dCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2320             Tcl_Obj *const *objv)
2321{
2322    Rappture::Unirect3d *dataPtr = (Rappture::Unirect3d *)clientData;
2323
2324    return dataPtr->LoadData(interp, objc, objv);
2325}
2326
2327Tcl_Interp *
2328initTcl()
2329{
2330
2331    /*
2332     * Ideally the connection is authenticated by nanoscale.  I still like the
2333     * idea of creating a non-safe master interpreter with a safe slave
2334     * interpreter.  Alias all the nanovis commands in the slave. That way we
2335     * can still run Tcl code within nanovis.  The eventual goal is to create
2336     * a test harness through the interpreter for nanovis.
2337     */
2338    Tcl_Interp *interp;
2339    interp = Tcl_CreateInterp();
2340    /*
2341    Tcl_MakeSafe(interp);
2342    */
2343    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
2344    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
2345    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
2346    if (FlowCmdInitProc(interp) != TCL_OK) {
2347        return NULL;
2348    }
2349    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
2350    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
2351    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
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.