source: branches/blt4/packages/vizservers/nanovis/Command.cpp @ 2742

Last change on this file since 2742 was 2742, checked in by gah, 11 years ago

sync with trunk

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