source: trunk/packages/vizservers/vtkvis/RpVtkRendererCmd.cpp @ 2509

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

Allow setting all axis units at the same time

  • Property svn:eol-style set to native
File size: 162.2 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2011, Purdue Research Foundation
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cstdlib>
9#include <cstdio>
10#include <cstring>
11#include <cerrno>
12#include <string>
13#include <sstream>
14#include <unistd.h>
15#include <sys/uio.h>
16#include <fcntl.h>
17#include <tcl.h>
18
19#include "Trace.h"
20#include "CmdProc.h"
21#include "RpVtkRendererCmd.h"
22#include "RpVtkRenderServer.h"
23#include "RpVtkRenderer.h"
24#include "PPMWriter.h"
25#include "TGAWriter.h"
26
27using namespace Rappture::VtkVis;
28
29static ssize_t
30SocketWrite(const void *bytes, size_t len)
31{
32    size_t ofs = 0;
33    ssize_t bytesWritten;
34    while ((bytesWritten = write(g_fdOut, (const char *)bytes + ofs, len - ofs)) > 0) {
35        ofs += bytesWritten;
36        if (ofs == len)
37            break;
38    }
39    if (bytesWritten < 0) {
40        ERROR("write: %s", strerror(errno));
41    }
42    return bytesWritten;
43}
44
45static size_t
46SocketRead(void *bytes, size_t len)
47{
48#ifdef notdef
49    size_t ofs = 0;
50    ssize_t bytesRead = 0;
51    while ((bytesRead = read(g_fdIn, bytes + ofs, len - ofs)) > 0) {
52        ofs += bytesRead;
53        if (ofs == len)
54            break;
55    }
56    TRACE("bytesRead: %lu", ofs);
57    return ofs;
58#else
59    size_t bytesRead = fread(bytes, 1, len, g_fIn);
60    TRACE("bytesRead: %lu", bytesRead);
61    return bytesRead;
62#endif
63}
64
65static int
66ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr)
67{
68    int result;
69
70    result = Tcl_Eval(interp, Tcl_DStringValue(dsPtr));
71    Tcl_DStringSetLength(dsPtr, 0);
72
73    return result;
74}
75
76static int
77GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
78{
79    int value;
80
81    if (Tcl_GetBooleanFromObj(interp, objPtr, &value) != TCL_OK) {
82        return TCL_ERROR;
83    }
84    *boolPtr = (bool)value;
85    return TCL_OK;
86}
87
88static int
89GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
90{
91    double value;
92
93    if (Tcl_GetDoubleFromObj(interp, objPtr, &value) != TCL_OK) {
94        return TCL_ERROR;
95    }
96    *valuePtr = (float)value;
97    return TCL_OK;
98}
99
100static int
101AxisColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
102            Tcl_Obj *const *objv)
103{
104    double color[3];
105    if (Tcl_GetDoubleFromObj(interp, objv[2], &color[0]) != TCL_OK ||
106        Tcl_GetDoubleFromObj(interp, objv[3], &color[1]) != TCL_OK ||
107        Tcl_GetDoubleFromObj(interp, objv[4], &color[2]) != TCL_OK) {
108        return TCL_ERROR;
109    }
110    g_renderer->setAxesColor(color);
111    return TCL_OK;
112}
113
114static int
115AxisFlyModeOp(ClientData clientData, Tcl_Interp *interp, int objc,
116              Tcl_Obj *const *objv)
117{
118    const char *string = Tcl_GetString(objv[2]);
119    char c = string[0];
120    Renderer::AxesFlyMode mode;
121    if ((c == 's') && (strcmp(string, "static_edges") == 0)) {
122        mode = Renderer::FLY_STATIC_EDGES;
123    } else if ((c == 's') && (strcmp(string, "static_triad") == 0)) {
124        mode = Renderer::FLY_STATIC_TRIAD;
125    } else if ((c == 'o') && (strcmp(string, "outer_edges") == 0)) {
126        mode = Renderer::FLY_OUTER_EDGES;
127    } else if ((c == 'f') && (strcmp(string, "furthest_triad") == 0)) {
128        mode = Renderer::FLY_FURTHEST_TRIAD;
129    } else if ((c == 'c') && (strcmp(string, "closest_triad") == 0)) {
130        mode = Renderer::FLY_CLOSEST_TRIAD;
131    } else {
132        Tcl_AppendResult(interp, "bad axis flymode option \"", string,
133                         "\": should be static_edges, static_triad, outer_edges, furthest_triad, or closest_triad", (char*)NULL);
134        return TCL_ERROR;
135    }
136    g_renderer->setAxesFlyMode(mode);
137    return TCL_OK;
138}
139
140static int
141AxisGridOp(ClientData clientData, Tcl_Interp *interp, int objc,
142           Tcl_Obj *const *objv)
143{
144    bool visible;
145    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
146        return TCL_ERROR;
147    }
148    const char *string = Tcl_GetString(objv[2]);
149    char c = string[0];
150    if ((c == 'x') && (strcmp(string, "x") == 0)) {
151        g_renderer->setAxisGridVisibility(Renderer::X_AXIS, visible);
152    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
153        g_renderer->setAxisGridVisibility(Renderer::Y_AXIS, visible);
154    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
155        g_renderer->setAxisGridVisibility(Renderer::Z_AXIS, visible);
156    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
157        g_renderer->setAxesGridVisibility(visible);
158    } else {
159        Tcl_AppendResult(interp, "bad axis option \"", string,
160                         "\": should be axisName visible", (char*)NULL);
161        return TCL_ERROR;
162    }
163    return TCL_OK;
164}
165
166static int
167AxisLabelsVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
168                    Tcl_Obj *const *objv)
169{
170    bool visible;
171    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
172        return TCL_ERROR;
173    }
174    const char *string = Tcl_GetString(objv[2]);
175    char c = string[0];
176    if ((c == 'x') && (strcmp(string, "x") == 0)) {
177        g_renderer->setAxisLabelVisibility(Renderer::X_AXIS, visible);
178    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
179        g_renderer->setAxisLabelVisibility(Renderer::Y_AXIS, visible);
180    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
181        g_renderer->setAxisLabelVisibility(Renderer::Z_AXIS, visible);
182    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
183        g_renderer->setAxesLabelVisibility(visible);
184    } else {
185        Tcl_AppendResult(interp, "bad axis option \"", string,
186                         "\": should be axisName visible", (char*)NULL);
187        return TCL_ERROR;
188    }
189    return TCL_OK;
190}
191
192static int
193AxisNameOp(ClientData clientData, Tcl_Interp *interp, int objc,
194           Tcl_Obj *const *objv)
195{
196    const char *title = Tcl_GetString(objv[3]);
197    if (strlen(title) > 30) {
198        Tcl_AppendResult(interp, "axis name \"", title,
199                         "\" is too long", (char*)NULL);
200        return TCL_ERROR;
201    }
202    const char *string = Tcl_GetString(objv[2]);
203    char c = string[0];
204    if ((c == 'x') && (strcmp(string, "x") == 0)) {
205        g_renderer->setAxisTitle(Renderer::X_AXIS, title);
206    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
207        g_renderer->setAxisTitle(Renderer::Y_AXIS, title);
208    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
209        g_renderer->setAxisTitle(Renderer::Z_AXIS, title);
210    } else {
211        Tcl_AppendResult(interp, "bad axis option \"", string,
212                         "\": should be axisName title", (char*)NULL);
213        return TCL_ERROR;
214    }
215    return TCL_OK;
216}
217
218static int
219AxisTicksVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
220                   Tcl_Obj *const *objv)
221{
222    bool visible;
223    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
224        return TCL_ERROR;
225    }
226    const char *string = Tcl_GetString(objv[2]);
227    char c = string[0];
228    if ((c == 'x') && (strcmp(string, "x") == 0)) {
229        g_renderer->setAxisTickVisibility(Renderer::X_AXIS, visible);
230    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
231        g_renderer->setAxisTickVisibility(Renderer::Y_AXIS, visible);
232    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
233        g_renderer->setAxisTickVisibility(Renderer::Z_AXIS, visible);
234    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
235        g_renderer->setAxesTickVisibility(visible);
236    } else {
237        Tcl_AppendResult(interp, "bad axis option \"", string,
238                         "\": should be axisName visible", (char*)NULL);
239        return TCL_ERROR;
240    }
241    return TCL_OK;
242}
243
244static int
245AxisTickPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
246                   Tcl_Obj *const *objv)
247{
248    const char *string = Tcl_GetString(objv[2]);
249    char c = string[0];
250    if ((c == 'i') && (strcmp(string, "inside") == 0)) {
251        g_renderer->setAxesTickPosition(Renderer::TICKS_INSIDE);
252    } else if ((c == 'o') && (strcmp(string, "outside") == 0)) {
253        g_renderer->setAxesTickPosition(Renderer::TICKS_OUTSIDE);
254    } else if ((c == 'b') && (strcmp(string, "both") == 0)) {
255        g_renderer->setAxesTickPosition(Renderer::TICKS_BOTH);
256    } else {
257        Tcl_AppendResult(interp, "bad axis option \"", string,
258                         "\": should be inside, outside or both", (char*)NULL);
259        return TCL_ERROR;
260    }
261    return TCL_OK;
262}
263
264static int
265AxisUnitsOp(ClientData clientData, Tcl_Interp *interp, int objc,
266            Tcl_Obj *const *objv)
267{
268    const char *units = Tcl_GetString(objv[3]);
269    if (strlen(units) > 10) {
270        Tcl_AppendResult(interp, "axis units name \"", units,
271                         "\" is too long", (char*)NULL);
272        return TCL_ERROR;
273    }
274    const char *string = Tcl_GetString(objv[2]);
275    char c = string[0];
276    if ((c == 'x') && (strcmp(string, "x") == 0)) {
277        g_renderer->setAxisUnits(Renderer::X_AXIS, units);
278    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
279        g_renderer->setAxisUnits(Renderer::Y_AXIS, units);
280    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
281        g_renderer->setAxisUnits(Renderer::Z_AXIS, units);
282    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
283        g_renderer->setAxisUnits(Renderer::X_AXIS, units);
284        g_renderer->setAxisUnits(Renderer::Y_AXIS, units);
285        g_renderer->setAxisUnits(Renderer::Z_AXIS, units);
286    } else {
287        Tcl_AppendResult(interp, "bad axis option \"", string,
288                         "\": should be axisName units", (char*)NULL);
289        return TCL_ERROR;
290    }
291    return TCL_OK;
292}
293
294static int
295AxisVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
296              Tcl_Obj *const *objv)
297{
298    bool visible;
299    if (GetBooleanFromObj(interp, objv[3], &visible) != TCL_OK) {
300        return TCL_ERROR;
301    }
302    const char *string = Tcl_GetString(objv[2]);
303    char c = string[0];
304    if ((c == 'x') && (strcmp(string, "x") == 0)) {
305        g_renderer->setAxisVisibility(Renderer::X_AXIS, visible);
306    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
307        g_renderer->setAxisVisibility(Renderer::Y_AXIS, visible);
308    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
309        g_renderer->setAxisVisibility(Renderer::Z_AXIS, visible);
310    } else if ((c == 'a') && (strcmp(string, "all") == 0)) {
311        g_renderer->setAxesVisibility(visible);
312    } else {
313        Tcl_AppendResult(interp, "bad axis option \"", string,
314                         "\": should be axisName visible", (char*)NULL);
315        return TCL_ERROR;
316    }
317    return TCL_OK;
318}
319
320static Rappture::CmdSpec axisOps[] = {
321    {"color",   1, AxisColorOp, 5, 5, "r g b"},
322    {"flymode", 1, AxisFlyModeOp, 3, 3, "mode"},
323    {"grid",    1, AxisGridOp, 4, 4, "axis bool"},
324    {"labels",  1, AxisLabelsVisibleOp, 4, 4, "axis bool"},
325    {"name",    1, AxisNameOp, 4, 4, "axis title"},
326    {"tickpos", 2, AxisTickPositionOp, 3, 3, "position"},
327    {"ticks",   2, AxisTicksVisibleOp, 4, 4, "axis bool"},
328    {"units",   1, AxisUnitsOp, 4, 4, "axis units"},
329    {"visible", 1, AxisVisibleOp, 4, 4, "axis bool"}
330};
331static int nAxisOps = NumCmdSpecs(axisOps);
332
333static int
334AxisCmd(ClientData clientData, Tcl_Interp *interp, int objc,
335        Tcl_Obj *const *objv)
336{
337    Tcl_ObjCmdProc *proc;
338
339    proc = Rappture::GetOpFromObj(interp, nAxisOps, axisOps,
340                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
341    if (proc == NULL) {
342        return TCL_ERROR;
343    }
344    return (*proc) (clientData, interp, objc, objv);
345}
346
347static int
348CameraModeOp(ClientData clientData, Tcl_Interp *interp, int objc,
349             Tcl_Obj *const *objv)
350{
351    Renderer::CameraMode mode;
352    const char *string = Tcl_GetString(objv[2]);
353    if ((strcmp(string, "persp") == 0)) {
354        mode = Renderer::PERSPECTIVE;
355    } else if ((strcmp(string, "ortho") == 0)) {
356        mode = Renderer::ORTHO;
357    } else if ((strcmp(string, "image") == 0)) {
358        mode = Renderer::IMAGE;
359    } else {
360        Tcl_AppendResult(interp, "bad camera mode option \"", string,
361                         "\": should be persp, ortho or image", (char*)NULL);
362        return TCL_ERROR;
363    }
364    g_renderer->setCameraMode(mode);
365    return TCL_OK;
366}
367
368static int
369CameraGetOp(ClientData clientData, Tcl_Interp *interp, int objc,
370            Tcl_Obj *const *objv)
371{
372    double pos[3];
373    double focalPt[3];
374    double viewUp[3];
375
376    g_renderer->getCameraOrientationAndPosition(pos, focalPt, viewUp);
377
378    char buf[256];
379    snprintf(buf, sizeof(buf), "nv>camera set %.12e %.12e %.12e %.12e %.12e %.12e %.12e %.12e %.12e\n",
380             pos[0], pos[1], pos[2], focalPt[0], focalPt[1], focalPt[2], viewUp[0], viewUp[1], viewUp[2]);
381
382    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
383
384    if (bytesWritten < 0) {
385        return TCL_ERROR;
386    }
387    return TCL_OK;
388}
389
390static int
391CameraOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
392               Tcl_Obj *const *objv)
393{
394    double quat[4];
395
396    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
397        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
398        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
399        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
400        return TCL_ERROR;
401    }
402
403    g_renderer->setCameraOrientation(quat);
404    return TCL_OK;
405}
406
407static int
408CameraOrthoOp(ClientData clientData, Tcl_Interp *interp, int objc,
409              Tcl_Obj *const *objv)
410{
411    const char *string = Tcl_GetString(objv[2]);
412
413    if (string[0] == 'p' && strcmp(string, "pixel") == 0) {
414        int x, y, width, height;
415        if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
416            Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK ||
417            Tcl_GetIntFromObj(interp, objv[5], &width) != TCL_OK ||
418            Tcl_GetIntFromObj(interp, objv[6], &height) != TCL_OK) {
419            return TCL_ERROR;
420        }
421        g_renderer->setCameraZoomRegionPixels(x, y, width, height);
422    } else if (string[0] == 'w' && strcmp(string, "world") == 0) {
423        double x, y, width, height;
424        if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK ||
425            Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK ||
426            Tcl_GetDoubleFromObj(interp, objv[5], &width) != TCL_OK ||
427            Tcl_GetDoubleFromObj(interp, objv[6], &height) != TCL_OK) {
428            return TCL_ERROR;
429        }
430        g_renderer->setCameraZoomRegion(x, y, width, height);
431    } else {
432        Tcl_AppendResult(interp, "bad camera ortho option \"", string,
433                         "\": should be pixel or world", (char*)NULL);
434        return TCL_ERROR;
435    }
436
437    return TCL_OK;
438}
439
440static int
441CameraPanOp(ClientData clientData, Tcl_Interp *interp, int objc,
442            Tcl_Obj *const *objv)
443{
444    double x, y;
445
446    if (Tcl_GetDoubleFromObj(interp, objv[2], &x) != TCL_OK ||
447        Tcl_GetDoubleFromObj(interp, objv[3], &y) != TCL_OK) {
448        return TCL_ERROR;
449    }
450
451    g_renderer->panCamera(x, y);
452    return TCL_OK;
453}
454
455static int
456CameraResetOp(ClientData clientData, Tcl_Interp *interp, int objc,
457              Tcl_Obj *const *objv)
458{
459    if (objc == 3) {
460        const char *string = Tcl_GetString(objv[2]);
461        char c = string[0];
462        if ((c != 'a') || (strcmp(string, "all") != 0)) {
463            Tcl_AppendResult(interp, "bad camera reset option \"", string,
464                         "\": should be all", (char*)NULL);
465            return TCL_ERROR;
466        }
467        g_renderer->resetCamera(true);
468    } else {
469        g_renderer->resetCamera(false);
470    }
471    return TCL_OK;
472}
473
474static int
475CameraRotateOp(ClientData clientData, Tcl_Interp *interp, int objc,
476               Tcl_Obj *const *objv)
477{
478    double yaw, pitch, roll;
479
480    if (Tcl_GetDoubleFromObj(interp, objv[2], &yaw) != TCL_OK ||
481        Tcl_GetDoubleFromObj(interp, objv[3], &pitch) != TCL_OK ||
482        Tcl_GetDoubleFromObj(interp, objv[4], &roll) != TCL_OK) {
483        return TCL_ERROR;
484    }
485
486    g_renderer->rotateCamera(yaw, pitch, roll);
487    return TCL_OK;
488}
489
490static int
491CameraSetOp(ClientData clientData, Tcl_Interp *interp, int objc,
492            Tcl_Obj *const *objv)
493{
494    double pos[3];
495    double focalPt[3];
496    double viewUp[3];
497
498    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
499        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
500        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK ||
501        Tcl_GetDoubleFromObj(interp, objv[5], &focalPt[0]) != TCL_OK ||
502        Tcl_GetDoubleFromObj(interp, objv[6], &focalPt[1]) != TCL_OK ||
503        Tcl_GetDoubleFromObj(interp, objv[7], &focalPt[2]) != TCL_OK ||
504        Tcl_GetDoubleFromObj(interp, objv[8], &viewUp[0]) != TCL_OK ||
505        Tcl_GetDoubleFromObj(interp, objv[9], &viewUp[1]) != TCL_OK ||
506        Tcl_GetDoubleFromObj(interp, objv[10], &viewUp[2]) != TCL_OK) {
507        return TCL_ERROR;
508    }
509
510    g_renderer->setCameraOrientationAndPosition(pos, focalPt, viewUp);
511    return TCL_OK;
512}
513
514static int
515CameraZoomOp(ClientData clientData, Tcl_Interp *interp, int objc,
516            Tcl_Obj *const *objv)
517{
518    double z;
519
520    if (Tcl_GetDoubleFromObj(interp, objv[2], &z) != TCL_OK) {
521        return TCL_ERROR;
522    }
523
524    g_renderer->zoomCamera(z);
525    return TCL_OK;
526}
527
528static Rappture::CmdSpec cameraOps[] = {
529    {"get", 1, CameraGetOp, 2, 2, ""},
530    {"mode", 1, CameraModeOp, 3, 3, "mode"},
531    {"orient", 3, CameraOrientOp, 6, 6, "qw qx qy qz"},
532    {"ortho", 1, CameraOrthoOp, 7, 7, "coordMode x y width height"},
533    {"pan", 1, CameraPanOp, 4, 4, "panX panY"},
534    {"reset", 2, CameraResetOp, 2, 3, "?all?"},
535    {"rotate", 2, CameraRotateOp, 5, 5, "angle angle angle"},
536    {"set", 1, CameraSetOp, 11, 11, "posX posY posZ focalPtX focalPtY focalPtZ viewUpX viewUpY viewUpZ"},
537    {"zoom", 1, CameraZoomOp, 3, 3, "zoomAmount"}
538};
539static int nCameraOps = NumCmdSpecs(cameraOps);
540
541static int
542CameraCmd(ClientData clientData, Tcl_Interp *interp, int objc,
543          Tcl_Obj *const *objv)
544{
545    Tcl_ObjCmdProc *proc;
546
547    proc = Rappture::GetOpFromObj(interp, nCameraOps, cameraOps,
548                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
549    if (proc == NULL) {
550        return TCL_ERROR;
551    }
552    return (*proc) (clientData, interp, objc, objv);
553}
554
555static int
556ColorMapAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
557              Tcl_Obj *const *objv)
558{
559    const char *name = Tcl_GetString(objv[2]);
560    int cmapc, omapc;
561    Tcl_Obj **cmapv = NULL;
562    Tcl_Obj **omapv = NULL;
563
564    if (Tcl_ListObjGetElements(interp, objv[3], &cmapc, &cmapv) != TCL_OK) {
565        return TCL_ERROR;
566    }
567    if ((cmapc % 4) != 0) {
568        Tcl_AppendResult(interp, "wrong # elements in colormap: should be ",
569                         "{ value r g b ... }", (char*)NULL);
570        return TCL_ERROR;
571    }
572
573    ColorMap *colorMap = new ColorMap(name);
574    colorMap->setNumberOfTableEntries(256);
575
576    for (int i = 0; i < cmapc; i += 4) {
577        double val[4];
578        for (int j = 0; j < 4; j++) {
579            if (Tcl_GetDoubleFromObj(interp, cmapv[i+j], &val[j]) != TCL_OK) {
580                delete colorMap;
581                return TCL_ERROR;
582            }
583            if ((val[j] < 0.0) || (val[j] > 1.0)) {
584                Tcl_AppendResult(interp, "bad colormap value \"",
585                                 Tcl_GetString(cmapv[i+j]),
586                                 "\": should be in the range [0,1]", (char*)NULL);
587                delete colorMap;
588                return TCL_ERROR;
589            }
590        }
591        ColorMap::ControlPoint cp;
592        cp.value = val[0];
593        for (int c = 0; c < 3; c++) {
594            cp.color[c] = val[c+1];
595        }
596        colorMap->addControlPoint(cp);
597    }
598
599    if (Tcl_ListObjGetElements(interp, objv[4], &omapc, &omapv) != TCL_OK) {
600        delete colorMap;
601        return TCL_ERROR;
602    }
603    if ((omapc % 2) != 0) {
604        Tcl_AppendResult(interp, "wrong # elements in opacitymap: should be ",
605                         "{ value alpha ... }", (char*)NULL);
606        delete colorMap;
607        return TCL_ERROR;
608    }
609    for (int i = 0; i < omapc; i += 2) {
610        double val[2];
611        for (int j = 0; j < 2; j++) {
612            if (Tcl_GetDoubleFromObj(interp, omapv[i+j], &val[j]) != TCL_OK) {
613                delete colorMap;
614                return TCL_ERROR;
615            }
616            if ((val[j] < 0.0) || (val[j] > 1.0)) {
617                Tcl_AppendResult(interp, "bad opacitymap value \"",
618                                 Tcl_GetString(omapv[i+j]),
619                                 "\": should be in the range [0,1]", (char*)NULL);
620                delete colorMap;
621                return TCL_ERROR;
622            }
623        }
624        ColorMap::OpacityControlPoint ocp;
625        ocp.value = val[0];
626        ocp.alpha = val[1];
627        colorMap->addOpacityControlPoint(ocp);
628    }
629
630    colorMap->build();
631    g_renderer->addColorMap(name, colorMap);
632    return TCL_OK;
633}
634
635static int
636ColorMapDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
637                 Tcl_Obj *const *objv)
638{
639    if (objc == 3) {
640        const char *name = Tcl_GetString(objv[2]);
641        g_renderer->deleteColorMap(name);
642    } else {
643        g_renderer->deleteColorMap("all");
644    }
645    return TCL_OK;
646}
647
648static Rappture::CmdSpec colorMapOps[] = {
649    { "add",    1, ColorMapAddOp,    5, 5, "colorMapName colormap alphamap"},
650    { "delete", 1, ColorMapDeleteOp, 2, 3, "?colorMapName?"}
651};
652static int nColorMapOps = NumCmdSpecs(colorMapOps);
653
654static int
655ColorMapCmd(ClientData clientData, Tcl_Interp *interp, int objc,
656            Tcl_Obj *const *objv)
657{
658    Tcl_ObjCmdProc *proc;
659
660    proc = Rappture::GetOpFromObj(interp, nColorMapOps, colorMapOps,
661                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
662    if (proc == NULL) {
663        return TCL_ERROR;
664    }
665    return (*proc) (clientData, interp, objc, objv);
666}
667
668static int
669Contour2DAddContourListOp(ClientData clientData, Tcl_Interp *interp, int objc,
670                          Tcl_Obj *const *objv)
671{
672    std::vector<double> contourList;
673
674    int clistc;
675    Tcl_Obj **clistv;
676
677    if (Tcl_ListObjGetElements(interp, objv[3], &clistc, &clistv) != TCL_OK) {
678        return TCL_ERROR;
679    }
680
681    for (int i = 0; i < clistc; i++) {
682        double val;
683        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
684            return TCL_ERROR;
685        }
686        contourList.push_back(val);
687    }
688
689    if (objc == 5) {
690        const char *name = Tcl_GetString(objv[4]);
691        if (!g_renderer->addContour2D(name, contourList)) {
692            Tcl_AppendResult(interp, "Failed to create contour2d", (char*)NULL);
693            return TCL_ERROR;
694        }
695    } else {
696        if (!g_renderer->addContour2D("all", contourList)) {
697            Tcl_AppendResult(interp, "Failed to create contour2d for one or more data sets", (char*)NULL);
698            return TCL_ERROR;
699        }
700    }
701    return TCL_OK;
702}
703
704static int
705Contour2DAddNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc,
706                          Tcl_Obj *const *objv)
707{
708    int numContours;
709    if (Tcl_GetIntFromObj(interp, objv[3], &numContours) != TCL_OK) {
710        return TCL_ERROR;
711    }
712    if (objc == 5) {
713        const char *name = Tcl_GetString(objv[4]);
714        if (!g_renderer->addContour2D(name, numContours)) {
715            Tcl_AppendResult(interp, "Failed to create contour2d", (char*)NULL);
716            return TCL_ERROR;
717        }
718    } else {
719       if (!g_renderer->addContour2D("all", numContours)) {
720            Tcl_AppendResult(interp, "Failed to create contour2d for one or more data sets", (char*)NULL);
721            return TCL_ERROR;
722       }
723    }
724    return TCL_OK;
725}
726
727static Rappture::CmdSpec contour2dAddOps[] = {
728    {"contourlist", 1, Contour2DAddContourListOp, 4, 5, "contourList ?dataSetName?"},
729    {"numcontours", 1, Contour2DAddNumContoursOp, 4, 5, "numContours ?dataSetName?"}
730};
731static int nContour2dAddOps = NumCmdSpecs(contour2dAddOps);
732
733static int
734Contour2DAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
735               Tcl_Obj *const *objv)
736{
737    Tcl_ObjCmdProc *proc;
738
739    proc = Rappture::GetOpFromObj(interp, nContour2dAddOps, contour2dAddOps,
740                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
741    if (proc == NULL) {
742        return TCL_ERROR;
743    }
744    return (*proc) (clientData, interp, objc, objv);
745}
746
747static int
748Contour2DDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
749                  Tcl_Obj *const *objv)
750{
751    if (objc == 3) {
752        const char *name = Tcl_GetString(objv[2]);
753        g_renderer->deleteContour2D(name);
754    } else {
755        g_renderer->deleteContour2D("all");
756    }
757    return TCL_OK;
758}
759
760static int
761Contour2DLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
762                    Tcl_Obj *const *objv)
763{
764    bool state;
765    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
766        return TCL_ERROR;
767    }
768    if (objc == 4) {
769        const char *name = Tcl_GetString(objv[3]);
770        g_renderer->setContour2DLighting(name, state);
771    } else {
772        g_renderer->setContour2DLighting("all", state);
773    }
774    return TCL_OK;
775}
776
777static int
778Contour2DLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
779                     Tcl_Obj *const *objv)
780{
781    float color[3];
782    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
783        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
784        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
785        return TCL_ERROR;
786    }
787    if (objc == 6) {
788        const char *name = Tcl_GetString(objv[5]);
789        g_renderer->setContour2DColor(name, color);
790    } else {
791        g_renderer->setContour2DColor("all", color);
792    }
793    return TCL_OK;
794}
795
796static int
797Contour2DLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
798                     Tcl_Obj *const *objv)
799{
800    float width;
801    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
802        return TCL_ERROR;
803    }
804    if (objc == 4) {
805        const char *name = Tcl_GetString(objv[3]);
806        g_renderer->setContour2DEdgeWidth(name, width);
807    } else {
808        g_renderer->setContour2DEdgeWidth("all", width);
809    }
810    return TCL_OK;
811}
812
813static int
814Contour2DOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
815                   Tcl_Obj *const *objv)
816{
817    double opacity;
818    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
819        return TCL_ERROR;
820    }
821    if (objc == 4) {
822        const char *name = Tcl_GetString(objv[3]);
823        g_renderer->setContour2DOpacity(name, opacity);
824    } else {
825        g_renderer->setContour2DOpacity("all", opacity);
826    }
827    return TCL_OK;
828}
829
830static int
831Contour2DOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
832                  Tcl_Obj *const *objv)
833{
834    double quat[4];
835    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
836        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
837        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
838        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
839        return TCL_ERROR;
840    }
841    if (objc == 7) {
842        const char *name = Tcl_GetString(objv[6]);
843        g_renderer->setContour2DOrientation(name, quat);
844    } else {
845        g_renderer->setContour2DOrientation("all", quat);
846    }
847    return TCL_OK;
848}
849
850static int
851Contour2DPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
852                    Tcl_Obj *const *objv)
853{
854    double pos[3];
855    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
856        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
857        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
858        return TCL_ERROR;
859    }
860    if (objc == 6) {
861        const char *name = Tcl_GetString(objv[5]);
862        g_renderer->setContour2DPosition(name, pos);
863    } else {
864        g_renderer->setContour2DPosition("all", pos);
865    }
866    return TCL_OK;
867}
868
869static int
870Contour2DScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
871                 Tcl_Obj *const *objv)
872{
873    double scale[3];
874    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
875        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
876        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
877        return TCL_ERROR;
878    }
879    if (objc == 6) {
880        const char *name = Tcl_GetString(objv[5]);
881        g_renderer->setContour2DScale(name, scale);
882    } else {
883        g_renderer->setContour2DScale("all", scale);
884    }
885    return TCL_OK;
886}
887
888static int
889Contour2DVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
890                   Tcl_Obj *const *objv)
891{
892    bool state;
893    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
894        return TCL_ERROR;
895    }
896    if (objc == 4) {
897        const char *name = Tcl_GetString(objv[3]);
898        g_renderer->setContour2DVisibility(name, state);
899    } else {
900        g_renderer->setContour2DVisibility("all", state);
901    }
902    return TCL_OK;
903}
904
905static Rappture::CmdSpec contour2dOps[] = {
906    {"add",       1, Contour2DAddOp, 4, 5, "oper value ?dataSetName?"},
907    {"color",     1, Contour2DLineColorOp, 5, 6, "r g b ?dataSetName?"},
908    {"delete",    1, Contour2DDeleteOp, 2, 3, "?dataSetName?"},
909    {"lighting",  3, Contour2DLightingOp, 3, 4, "bool ?dataSetName?"},
910    {"linecolor", 5, Contour2DLineColorOp, 5, 6, "r g b ?dataSetName?"},
911    {"linewidth", 5, Contour2DLineWidthOp, 3, 4, "width ?dataSetName?"},
912    {"opacity",   2, Contour2DOpacityOp, 3, 4, "value ?dataSetName?"},
913    {"orient",    2, Contour2DOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
914    {"pos",       1, Contour2DPositionOp, 5, 6, "x y z ?dataSetName?"},
915    {"scale",     1, Contour2DScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
916    {"visible",   1, Contour2DVisibleOp, 3, 4, "bool ?dataSetName?"}
917};
918static int nContour2dOps = NumCmdSpecs(contour2dOps);
919
920static int
921Contour2DCmd(ClientData clientData, Tcl_Interp *interp, int objc,
922             Tcl_Obj *const *objv)
923{
924    Tcl_ObjCmdProc *proc;
925
926    proc = Rappture::GetOpFromObj(interp, nContour2dOps, contour2dOps,
927                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
928    if (proc == NULL) {
929        return TCL_ERROR;
930    }
931    return (*proc) (clientData, interp, objc, objv);
932}
933
934static int
935Contour3DAddContourListOp(ClientData clientData, Tcl_Interp *interp, int objc,
936                          Tcl_Obj *const *objv)
937{
938    std::vector<double> contourList;
939
940    int clistc;
941    Tcl_Obj **clistv;
942
943    if (Tcl_ListObjGetElements(interp, objv[3], &clistc, &clistv) != TCL_OK) {
944        return TCL_ERROR;
945    }
946
947    for (int i = 0; i < clistc; i++) {
948        double val;
949        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
950            return TCL_ERROR;
951        }
952        contourList.push_back(val);
953    }
954
955    if (objc == 5) {
956        const char *name = Tcl_GetString(objv[4]);
957        if (!g_renderer->addContour3D(name, contourList)) {
958            Tcl_AppendResult(interp, "Failed to create contour3d", (char*)NULL);
959            return TCL_ERROR;
960        }
961    } else {
962        if (!g_renderer->addContour3D("all", contourList)) {
963            Tcl_AppendResult(interp, "Failed to create contour3d for one or more data sets", (char*)NULL);
964            return TCL_ERROR;
965        }
966    }
967    return TCL_OK;
968}
969
970static int
971Contour3DAddNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc,
972                          Tcl_Obj *const *objv)
973{
974    int numContours;
975    if (Tcl_GetIntFromObj(interp, objv[3], &numContours) != TCL_OK) {
976        return TCL_ERROR;
977    }
978    if (objc == 5) {
979        const char *name = Tcl_GetString(objv[4]);
980        if (!g_renderer->addContour3D(name, numContours)) {
981            Tcl_AppendResult(interp, "Failed to create contour3d", (char*)NULL);
982            return TCL_ERROR;
983        }
984    } else {
985        if (!g_renderer->addContour3D("all", numContours)) {
986            Tcl_AppendResult(interp, "Failed to create contour3d for one or more data sets", (char*)NULL);
987            return TCL_ERROR;
988        }
989    }
990    return TCL_OK;
991}
992
993static Rappture::CmdSpec contour3dAddOps[] = {
994    {"contourlist", 1, Contour3DAddContourListOp, 4, 5, "contourList ?dataSetName?"},
995    {"numcontours", 1, Contour3DAddNumContoursOp, 4, 5, "numContours ?dataSetName?"}
996};
997static int nContour3dAddOps = NumCmdSpecs(contour3dAddOps);
998
999static int
1000Contour3DAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1001               Tcl_Obj *const *objv)
1002{
1003    Tcl_ObjCmdProc *proc;
1004
1005    proc = Rappture::GetOpFromObj(interp, nContour3dAddOps, contour3dAddOps,
1006                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1007    if (proc == NULL) {
1008        return TCL_ERROR;
1009    }
1010    return (*proc) (clientData, interp, objc, objv);
1011}
1012
1013static int
1014Contour3DColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1015                 Tcl_Obj *const *objv)
1016{
1017    float color[3];
1018    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
1019        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
1020        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
1021        return TCL_ERROR;
1022    }
1023    if (objc == 6) {
1024        const char *name = Tcl_GetString(objv[5]);
1025        g_renderer->setContour3DColor(name, color);
1026    } else {
1027        g_renderer->setContour3DColor("all", color);
1028    }
1029    return TCL_OK;
1030}
1031
1032static int
1033Contour3DColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
1034                    Tcl_Obj *const *objv)
1035{
1036    const char *colorMapName = Tcl_GetString(objv[2]);
1037    if (objc == 4) {
1038        const char *dataSetName = Tcl_GetString(objv[3]);
1039        g_renderer->setContour3DColorMap(dataSetName, colorMapName);
1040    } else {
1041        g_renderer->setContour3DColorMap("all", colorMapName);
1042    }
1043    return TCL_OK;
1044}
1045
1046static int
1047Contour3DDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1048                  Tcl_Obj *const *objv)
1049{
1050    if (objc == 3) {
1051        const char *name = Tcl_GetString(objv[2]);
1052        g_renderer->deleteContour3D(name);
1053    } else {
1054        g_renderer->deleteContour3D("all");
1055    }
1056    return TCL_OK;
1057}
1058
1059static int
1060Contour3DEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1061                          Tcl_Obj *const *objv)
1062{
1063    bool state;
1064    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1065        return TCL_ERROR;
1066    }
1067    if (objc == 4) {
1068        const char *name = Tcl_GetString(objv[3]);
1069        g_renderer->setContour3DEdgeVisibility(name, state);
1070    } else {
1071        g_renderer->setContour3DEdgeVisibility("all", state);
1072    }
1073    return TCL_OK;
1074}
1075
1076static int
1077Contour3DLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1078                    Tcl_Obj *const *objv)
1079{
1080    bool state;
1081    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1082        return TCL_ERROR;
1083    }
1084    if (objc == 4) {
1085        const char *name = Tcl_GetString(objv[3]);
1086        g_renderer->setContour3DLighting(name, state);
1087    } else {
1088        g_renderer->setContour3DLighting("all", state);
1089    }
1090    return TCL_OK;
1091}
1092
1093static int
1094Contour3DLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1095                     Tcl_Obj *const *objv)
1096{
1097    float color[3];
1098    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
1099        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
1100        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
1101        return TCL_ERROR;
1102    }
1103    if (objc == 6) {
1104        const char *name = Tcl_GetString(objv[5]);
1105        g_renderer->setContour3DEdgeColor(name, color);
1106    } else {
1107        g_renderer->setContour3DEdgeColor("all", color);
1108    }
1109    return TCL_OK;
1110}
1111
1112static int
1113Contour3DLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
1114                     Tcl_Obj *const *objv)
1115{
1116    float width;
1117    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
1118        return TCL_ERROR;
1119    }
1120    if (objc == 4) {
1121        const char *name = Tcl_GetString(objv[3]);
1122        g_renderer->setContour3DEdgeWidth(name, width);
1123    } else {
1124        g_renderer->setContour3DEdgeWidth("all", width);
1125    }
1126    return TCL_OK;
1127}
1128
1129static int
1130Contour3DOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1131                   Tcl_Obj *const *objv)
1132{
1133    double opacity;
1134    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
1135        return TCL_ERROR;
1136    }
1137    if (objc == 4) {
1138        const char *name = Tcl_GetString(objv[3]);
1139        g_renderer->setContour3DOpacity(name, opacity);
1140    } else {
1141        g_renderer->setContour3DOpacity("all", opacity);
1142    }
1143    return TCL_OK;
1144}
1145
1146static int
1147Contour3DOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
1148                  Tcl_Obj *const *objv)
1149{
1150    double quat[4];
1151    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
1152        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
1153        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
1154        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
1155        return TCL_ERROR;
1156    }
1157    if (objc == 7) {
1158        const char *name = Tcl_GetString(objv[6]);
1159        g_renderer->setContour3DOrientation(name, quat);
1160    } else {
1161        g_renderer->setContour3DOrientation("all", quat);
1162    }
1163    return TCL_OK;
1164}
1165
1166static int
1167Contour3DPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
1168                    Tcl_Obj *const *objv)
1169{
1170    double pos[3];
1171    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
1172        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
1173        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
1174        return TCL_ERROR;
1175    }
1176    if (objc == 6) {
1177        const char *name = Tcl_GetString(objv[5]);
1178        g_renderer->setContour3DPosition(name, pos);
1179    } else {
1180        g_renderer->setContour3DPosition("all", pos);
1181    }
1182    return TCL_OK;
1183}
1184
1185static int
1186Contour3DScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1187                 Tcl_Obj *const *objv)
1188{
1189    double scale[3];
1190    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
1191        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
1192        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
1193        return TCL_ERROR;
1194    }
1195    if (objc == 6) {
1196        const char *name = Tcl_GetString(objv[5]);
1197        g_renderer->setContour3DScale(name, scale);
1198    } else {
1199        g_renderer->setContour3DScale("all", scale);
1200    }
1201    return TCL_OK;
1202}
1203
1204static int
1205Contour3DVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1206                   Tcl_Obj *const *objv)
1207{
1208    bool state;
1209    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1210        return TCL_ERROR;
1211    }
1212    if (objc == 4) {
1213        const char *name = Tcl_GetString(objv[3]);
1214        g_renderer->setContour3DVisibility(name, state);
1215    } else {
1216        g_renderer->setContour3DVisibility("all", state);
1217    }
1218    return TCL_OK;
1219}
1220
1221static int
1222Contour3DWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc,
1223                     Tcl_Obj *const *objv)
1224{
1225    bool state;
1226    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1227        return TCL_ERROR;
1228    }
1229    if (objc == 4) {
1230        const char *name = Tcl_GetString(objv[3]);
1231        g_renderer->setContour3DWireframe(name, state);
1232    } else {
1233        g_renderer->setContour3DWireframe("all", state);
1234    }
1235    return TCL_OK;
1236}
1237
1238static Rappture::CmdSpec contour3dOps[] = {
1239    {"add",       1, Contour3DAddOp, 4, 5, "oper value ?dataSetName?"},
1240    {"ccolor",    2, Contour3DColorOp, 5, 6, "r g b ?dataSetName?"},
1241    {"colormap",  2, Contour3DColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
1242    {"delete",    1, Contour3DDeleteOp, 2, 3, "?dataSetName?"},
1243    {"edges",     1, Contour3DEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
1244    {"lighting",  3, Contour3DLightingOp, 3, 4, "bool ?dataSetName?"},
1245    {"linecolor", 5, Contour3DLineColorOp, 5, 6, "r g b ?dataSetName?"},
1246    {"linewidth", 5, Contour3DLineWidthOp, 3, 4, "width ?dataSetName?"},
1247    {"opacity",   2, Contour3DOpacityOp, 3, 4, "value ?dataSetName?"},
1248    {"orient",    2, Contour3DOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
1249    {"pos",       1, Contour3DPositionOp, 5, 6, "x y z ?dataSetName?"},
1250    {"scale",     1, Contour3DScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
1251    {"visible",   1, Contour3DVisibleOp, 3, 4, "bool ?dataSetName?"},
1252    {"wireframe", 1, Contour3DWireframeOp, 3, 4, "bool ?dataSetName?"}
1253};
1254static int nContour3dOps = NumCmdSpecs(contour3dOps);
1255
1256static int
1257Contour3DCmd(ClientData clientData, Tcl_Interp *interp, int objc,
1258             Tcl_Obj *const *objv)
1259{
1260    Tcl_ObjCmdProc *proc;
1261
1262    proc = Rappture::GetOpFromObj(interp, nContour3dOps, contour3dOps,
1263                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
1264    if (proc == NULL) {
1265        return TCL_ERROR;
1266    }
1267    return (*proc) (clientData, interp, objc, objv);
1268}
1269
1270static int
1271DataSetActiveScalarsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1272                       Tcl_Obj *const *objv)
1273{
1274    const char *scalarName = Tcl_GetString(objv[2]);
1275    if (objc == 4) {
1276        const char *name = Tcl_GetString(objv[3]);
1277        if (!g_renderer->setDataSetActiveScalars(name, scalarName)) {
1278            Tcl_AppendResult(interp, "scalar \"", scalarName,
1279                         "\" not found", (char*)NULL);
1280            return TCL_ERROR;
1281        }
1282    } else {
1283        if (!g_renderer->setDataSetActiveScalars("all", scalarName)) {
1284            Tcl_AppendResult(interp, "scalar \"", scalarName,
1285                         "\" not found in one or more data sets", (char*)NULL);
1286            return TCL_ERROR;
1287        }
1288    }
1289    return TCL_OK;
1290}
1291
1292static int
1293DataSetActiveVectorsOp(ClientData clientData, Tcl_Interp *interp, int objc,
1294                       Tcl_Obj *const *objv)
1295{
1296    const char *vectorName = Tcl_GetString(objv[2]);
1297    if (objc == 4) {
1298        const char *name = Tcl_GetString(objv[3]);
1299        if (!g_renderer->setDataSetActiveVectors(name, vectorName)) {
1300            Tcl_AppendResult(interp, "vector \"", vectorName,
1301                         "\" not found", (char*)NULL);
1302            return TCL_ERROR;
1303        }
1304    } else {
1305        if (!g_renderer->setDataSetActiveVectors("all", vectorName)) {
1306            Tcl_AppendResult(interp, "vector \"", vectorName,
1307                         "\" not found in one or more data sets", (char*)NULL);
1308            return TCL_ERROR;
1309        }
1310    }
1311    return TCL_OK;
1312}
1313
1314static int
1315DataSetAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1316             Tcl_Obj *const *objv)
1317{
1318    const char *name = Tcl_GetString(objv[2]);
1319    const char *string = Tcl_GetString(objv[3]);
1320    char c = string[0];
1321    if ((c != 'd') || (strcmp(string, "data") != 0)) {
1322        Tcl_AppendResult(interp, "bad dataset option \"", string,
1323                         "\": should be data", (char*)NULL);
1324        return TCL_ERROR;
1325    }
1326    string = Tcl_GetString(objv[4]);
1327    c = string[0];
1328    if ((c != 'f') || (strcmp(string, "follows") != 0)) {
1329        Tcl_AppendResult(interp, "bad dataset option \"", string,
1330                         "\": should be follows", (char*)NULL);
1331        return TCL_ERROR;
1332    }
1333    int nbytes = 0;
1334    if (Tcl_GetIntFromObj(interp, objv[5], &nbytes) != TCL_OK ||
1335        nbytes < 0) {
1336        return TCL_ERROR;
1337    }
1338    char *data = (char *)malloc(nbytes);
1339    size_t bytesRead = SocketRead(data, nbytes);
1340    if (bytesRead < 0) {
1341        free(data);
1342        return TCL_ERROR;
1343    }
1344    g_renderer->addDataSet(name);
1345    g_renderer->setData(name, data, nbytes);
1346    free(data);
1347    return TCL_OK;
1348}
1349
1350static int
1351DataSetDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1352                Tcl_Obj *const *objv)
1353{
1354    if (objc == 3) {
1355        const char *name = Tcl_GetString(objv[2]);
1356        TRACE("Deleting dataset %s", name);
1357        g_renderer->deleteDataSet(name);
1358    } else {
1359        g_renderer->deleteDataSet("all");
1360    }
1361    return TCL_OK;
1362}
1363
1364static int
1365DataSetGetScalarPixelOp(ClientData clientData, Tcl_Interp *interp, int objc,
1366                        Tcl_Obj *const *objv)
1367{
1368    const char *name = Tcl_GetString(objv[5]);
1369    int x, y;
1370    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
1371        Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
1372        return TCL_ERROR;
1373    }
1374    double value;
1375    if (!g_renderer->getScalarValueAtPixel(name, x, y, &value)) {
1376        Tcl_AppendResult(interp, "Pixel out of dataset bounds or no scalar data available", (char*)NULL);
1377        return TCL_ERROR;
1378    }
1379
1380    char buf[256];
1381    snprintf(buf, sizeof(buf), "nv>dataset scalar pixel %d %d %g %s\n", x, y, value, name);
1382
1383    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
1384
1385    if (bytesWritten < 0) {
1386        return TCL_ERROR;
1387    }
1388    return TCL_OK;
1389}
1390
1391static int
1392DataSetGetScalarWorldOp(ClientData clientData, Tcl_Interp *interp, int objc,
1393                        Tcl_Obj *const *objv)
1394{
1395    const char *name = Tcl_GetString(objv[6]);
1396    double x, y, z;
1397    if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK ||
1398        Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK ||
1399        Tcl_GetDoubleFromObj(interp, objv[5], &z) != TCL_OK) {
1400        return TCL_ERROR;
1401    }
1402    double value;
1403    if (!g_renderer->getScalarValue(name, x, y, z, &value)) {
1404        Tcl_AppendResult(interp, "Coordinate out of dataset bounds or no scalar data available", (char*)NULL);
1405        return TCL_ERROR;
1406    }
1407
1408    char buf[256];
1409    snprintf(buf, sizeof(buf), "nv>dataset scalar world %g %g %g %g %s\n", x, y, z, value, name);
1410
1411    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
1412
1413    if (bytesWritten < 0) {
1414        return TCL_ERROR;
1415    }
1416    return TCL_OK;
1417}
1418
1419static Rappture::CmdSpec dataSetGetScalarOps[] = {
1420    {"pixel", 1, DataSetGetScalarPixelOp, 6, 6, "x y dataSetName"},
1421    {"world", 1, DataSetGetScalarWorldOp, 7, 7, "x y z dataSetName"}
1422};
1423static int nDataSetGetScalarOps = NumCmdSpecs(dataSetGetScalarOps);
1424
1425static int
1426DataSetGetScalarOp(ClientData clientData, Tcl_Interp *interp, int objc,
1427                   Tcl_Obj *const *objv)
1428{
1429    Tcl_ObjCmdProc *proc;
1430
1431    proc = Rappture::GetOpFromObj(interp, nDataSetGetScalarOps, dataSetGetScalarOps,
1432                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1433    if (proc == NULL) {
1434        return TCL_ERROR;
1435    }
1436    return (*proc) (clientData, interp, objc, objv);
1437}
1438
1439static int
1440DataSetGetVectorPixelOp(ClientData clientData, Tcl_Interp *interp, int objc,
1441                        Tcl_Obj *const *objv)
1442{
1443    const char *name = Tcl_GetString(objv[5]);
1444    int x, y;
1445    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
1446        Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
1447        return TCL_ERROR;
1448    }
1449    double value[3];
1450    if (!g_renderer->getVectorValueAtPixel(name, x, y, value)) {
1451        Tcl_AppendResult(interp, "Pixel out of dataset bounds or no vector data available", (char*)NULL);
1452        return TCL_ERROR;
1453    }
1454
1455    char buf[256];
1456    snprintf(buf, sizeof(buf), "nv>dataset vector pixel %d %d %g %g %g %s\n", x, y,
1457             value[0], value[1], value[2], name);
1458
1459    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
1460
1461    if (bytesWritten < 0) {
1462        return TCL_ERROR;
1463    }
1464    return TCL_OK;
1465}
1466
1467static int
1468DataSetGetVectorWorldOp(ClientData clientData, Tcl_Interp *interp, int objc,
1469                        Tcl_Obj *const *objv)
1470{
1471    const char *name = Tcl_GetString(objv[6]);
1472    double x, y, z;
1473    if (Tcl_GetDoubleFromObj(interp, objv[3], &x) != TCL_OK ||
1474        Tcl_GetDoubleFromObj(interp, objv[4], &y) != TCL_OK ||
1475        Tcl_GetDoubleFromObj(interp, objv[5], &z) != TCL_OK) {
1476        return TCL_ERROR;
1477    }
1478    double value[3];
1479    if (!g_renderer->getVectorValue(name, x, y, z, value)) {
1480        Tcl_AppendResult(interp, "Coordinate out of dataset bounds or no vector data available", (char*)NULL);
1481        return TCL_ERROR;
1482    }
1483
1484    char buf[256];
1485    snprintf(buf, sizeof(buf), "nv>dataset vector world %g %g %g %g %g %g %s\n", x, y, z,
1486             value[0], value[1], value[2], name);
1487
1488    ssize_t bytesWritten = SocketWrite(buf, strlen(buf));
1489
1490    if (bytesWritten < 0) {
1491        return TCL_ERROR;
1492    }
1493    return TCL_OK;
1494}
1495
1496static Rappture::CmdSpec dataSetGetVectorOps[] = {
1497    {"pixel", 1, DataSetGetVectorPixelOp, 6, 6, "x y dataSetName"},
1498    {"world", 1, DataSetGetVectorWorldOp, 7, 7, "x y z dataSetName"}
1499};
1500static int nDataSetGetVectorOps = NumCmdSpecs(dataSetGetVectorOps);
1501
1502static int
1503DataSetGetVectorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1504                   Tcl_Obj *const *objv)
1505{
1506    Tcl_ObjCmdProc *proc;
1507
1508    proc = Rappture::GetOpFromObj(interp, nDataSetGetVectorOps, dataSetGetVectorOps,
1509                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
1510    if (proc == NULL) {
1511        return TCL_ERROR;
1512    }
1513    return (*proc) (clientData, interp, objc, objv);
1514}
1515
1516static int
1517DataSetMapRangeOp(ClientData clientData, Tcl_Interp *interp, int objc,
1518                  Tcl_Obj *const *objv)
1519{
1520    const char *value = Tcl_GetString(objv[2]);
1521    if (strcmp(value, "all") == 0) {
1522        g_renderer->setUseCumulativeDataRange(true);
1523    } else if (strcmp(value, "visible") == 0) {
1524        g_renderer->setUseCumulativeDataRange(true, true);
1525    } else if (strcmp(value, "separate") == 0) {
1526        g_renderer->setUseCumulativeDataRange(false);
1527    } else {
1528        Tcl_AppendResult(interp, "bad maprange option \"", value,
1529                         "\": should be all, visible, or separate", (char*)NULL);
1530        return TCL_ERROR;
1531    }
1532    return TCL_OK;
1533}
1534
1535static int
1536DataSetNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
1537               Tcl_Obj *const *objv)
1538{
1539    std::vector<std::string> dataSets;
1540    g_renderer->getDataSetNames(dataSets);
1541    std::ostringstream oss;
1542    size_t len = 0;
1543    oss << "nv>dataset names {";
1544    len += 18;
1545    for (size_t i = 0; i < dataSets.size(); i++) {
1546        oss << "\"" << dataSets[i] << "\"";
1547        len += 2 + dataSets[i].length();
1548        if (i < dataSets.size() - 1) {
1549            oss << " ";
1550            len++;
1551        }
1552    }
1553    oss << "}\n";
1554    len += 2;
1555
1556    size_t bytesWritten = SocketWrite(oss.str().c_str(), len);
1557
1558    if (bytesWritten < 0) {
1559        return TCL_ERROR;
1560    }
1561    return TCL_OK;
1562}
1563
1564static int
1565DataSetOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1566                 Tcl_Obj *const *objv)
1567{
1568    double opacity;
1569    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
1570        return TCL_ERROR;
1571    }
1572    if (objc == 4) {
1573        const char *name = Tcl_GetString(objv[3]);
1574        g_renderer->setDataSetOpacity(name, opacity);
1575    } else {
1576        g_renderer->setDataSetOpacity("all", opacity);
1577    }
1578    return TCL_OK;
1579}
1580
1581static int
1582DataSetOutlineOp(ClientData clientData, Tcl_Interp *interp, int objc,
1583                 Tcl_Obj *const *objv)
1584{
1585    bool state;
1586    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1587        return TCL_ERROR;
1588    }
1589    if (objc == 4) {
1590        const char *name = Tcl_GetString(objv[3]);
1591        g_renderer->setDataSetShowBounds(name, state);
1592    } else {
1593        g_renderer->setDataSetShowBounds("all", state);
1594    }
1595    return TCL_OK;
1596}
1597
1598static int
1599DataSetOutlineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1600                      Tcl_Obj *const *objv)
1601{
1602    float color[3];
1603    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
1604        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
1605        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
1606        return TCL_ERROR;
1607    }
1608    if (objc == 6) {
1609        const char *name = Tcl_GetString(objv[5]);
1610        g_renderer->setDataSetOutlineColor(name, color);
1611    } else {
1612        g_renderer->setDataSetOutlineColor("all", color);
1613    }
1614    return TCL_OK;
1615}
1616
1617static int
1618DataSetVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1619                 Tcl_Obj *const *objv)
1620{
1621    bool state;
1622    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1623        return TCL_ERROR;
1624    }
1625    if (objc == 4) {
1626        const char *name = Tcl_GetString(objv[3]);
1627        g_renderer->setDataSetVisibility(name, state);
1628    } else {
1629        g_renderer->setDataSetVisibility("all", state);
1630    }
1631    return TCL_OK;
1632}
1633
1634static Rappture::CmdSpec dataSetOps[] = {
1635    {"add",       1, DataSetAddOp, 6, 6, "name data follows nBytes"},
1636    {"color",     1, DataSetOutlineColorOp, 5, 6, "r g b ?name?"},
1637    {"delete",    1, DataSetDeleteOp, 2, 3, "?name?"},
1638    {"getscalar", 4, DataSetGetScalarOp, 6, 7, "oper x y ?z? name"},
1639    {"getvector", 4, DataSetGetVectorOp, 6, 7, "oper x y ?z? name"},
1640    {"maprange",  1, DataSetMapRangeOp, 3, 3, "value"},
1641    {"names",     1, DataSetNamesOp, 2, 2, ""},
1642    {"opacity",   2, DataSetOpacityOp, 3, 4, "value ?name?"},
1643    {"outline",   2, DataSetOutlineOp, 3, 4, "bool ?name?"},
1644    {"scalar",    1, DataSetActiveScalarsOp, 3, 4, "scalarName ?name?"},
1645    {"vector",    2, DataSetActiveVectorsOp, 3, 4, "vectorName ?name?"},
1646    {"visible",   2, DataSetVisibleOp, 3, 4, "bool ?name?"}
1647};
1648static int nDataSetOps = NumCmdSpecs(dataSetOps);
1649
1650static int
1651DataSetCmd(ClientData clientData, Tcl_Interp *interp, int objc,
1652           Tcl_Obj *const *objv)
1653{
1654    Tcl_ObjCmdProc *proc;
1655
1656    proc = Rappture::GetOpFromObj(interp, nDataSetOps, dataSetOps,
1657                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
1658    if (proc == NULL) {
1659        return TCL_ERROR;
1660    }
1661    return (*proc) (clientData, interp, objc, objv);
1662}
1663
1664static int
1665GlyphsAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
1666            Tcl_Obj *const *objv)
1667{
1668    Glyphs::GlyphShape shape;
1669
1670    const char *shapeOpt = Tcl_GetString(objv[2]);
1671    if (shapeOpt[0] == 'a' && strcmp(shapeOpt, "arrow") == 0) {
1672        shape = Glyphs::ARROW;
1673    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cone") == 0) {
1674        shape = Glyphs::CONE;
1675    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cube") == 0) {
1676        shape = Glyphs::CUBE;
1677    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cylinder") == 0) {
1678        shape = Glyphs::CYLINDER;
1679    } else if (shapeOpt[0] == 'd' && strcmp(shapeOpt, "dodecahedron") == 0) {
1680        shape = Glyphs::DODECAHEDRON;
1681    } else if (shapeOpt[0] == 'i' && strcmp(shapeOpt, "icosahedron") == 0) {
1682        shape = Glyphs::ICOSAHEDRON;
1683    } else if (shapeOpt[0] == 'l' && strcmp(shapeOpt, "line") == 0) {
1684        shape = Glyphs::LINE;
1685    } else if (shapeOpt[0] == 'o' && strcmp(shapeOpt, "octahedron") == 0) {
1686        shape = Glyphs::OCTAHEDRON;
1687    } else if (shapeOpt[0] == 's' && strcmp(shapeOpt, "sphere") == 0) {
1688        shape = Glyphs::SPHERE;
1689    } else if (shapeOpt[0] == 't' && strcmp(shapeOpt, "tetrahedron") == 0) {
1690        shape = Glyphs::TETRAHEDRON;
1691    } else {
1692        Tcl_AppendResult(interp, "bad shape option \"", shapeOpt,
1693                         "\": should be one of: 'arrow', 'cone', 'cube', 'cylinder', 'dodecahedron', 'icosahedron', 'octahedron', 'sphere', 'tetrahedron'", (char*)NULL);
1694        return TCL_ERROR;
1695    }
1696
1697    if (objc == 4) {
1698        const char *name = Tcl_GetString(objv[3]);
1699        if (!g_renderer->addGlyphs(name, shape)) {
1700            Tcl_AppendResult(interp, "Failed to create glyphs", (char*)NULL);
1701            return TCL_ERROR;
1702        }
1703    } else {
1704        if (!g_renderer->addGlyphs("all", shape)) {
1705            Tcl_AppendResult(interp, "Failed to create glyphs for one or more data sets", (char*)NULL);
1706            return TCL_ERROR;
1707        }
1708    }
1709    return TCL_OK;
1710}
1711
1712static int
1713GlyphsColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1714              Tcl_Obj *const *objv)
1715{
1716    float color[3];
1717    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
1718        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
1719        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
1720        return TCL_ERROR;
1721    }
1722    if (objc == 6) {
1723        const char *name = Tcl_GetString(objv[5]);
1724        g_renderer->setGlyphsColor(name, color);
1725    } else {
1726        g_renderer->setGlyphsColor("all", color);
1727    }
1728    return TCL_OK;
1729}
1730
1731static int
1732GlyphsColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
1733                 Tcl_Obj *const *objv)
1734{
1735    const char *colorMapName = Tcl_GetString(objv[2]);
1736    if (objc == 4) {
1737        const char *dataSetName = Tcl_GetString(objv[3]);
1738        g_renderer->setGlyphsColorMap(dataSetName, colorMapName);
1739    } else {
1740        g_renderer->setGlyphsColorMap("all", colorMapName);
1741    }
1742    return TCL_OK;
1743}
1744
1745static int
1746GlyphsColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc,
1747                  Tcl_Obj *const *objv)
1748{
1749    Glyphs::ColorMode mode;
1750    const char *str = Tcl_GetString(objv[2]);
1751    if (str[0] == 'c' && strcmp(str, "ccolor") == 0) {
1752        mode = Glyphs::COLOR_CONSTANT;
1753    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
1754        mode = Glyphs::COLOR_BY_SCALAR;
1755    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
1756        mode = Glyphs::COLOR_BY_VECTOR_MAGNITUDE;
1757    } else {
1758        Tcl_AppendResult(interp, "bad color mode option \"", str,
1759                         "\": should be one of: 'scalar', 'vmag', 'ccolor'", (char*)NULL);
1760        return TCL_ERROR;
1761    }
1762    if (objc == 4) {
1763        const char *name = Tcl_GetString(objv[3]);
1764        g_renderer->setGlyphsColorMode(name, mode);
1765    } else {
1766        g_renderer->setGlyphsColorMode("all", mode);
1767    }
1768    return TCL_OK;
1769}
1770
1771static int
1772GlyphsDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
1773               Tcl_Obj *const *objv)
1774{
1775    if (objc == 3) {
1776        const char *name = Tcl_GetString(objv[2]);
1777        g_renderer->deleteGlyphs(name);
1778    } else {
1779        g_renderer->deleteGlyphs("all");
1780    }
1781    return TCL_OK;
1782}
1783
1784static int
1785GlyphsEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1786                       Tcl_Obj *const *objv)
1787{
1788    bool state;
1789    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1790        return TCL_ERROR;
1791    }
1792    if (objc == 4) {
1793        const char *name = Tcl_GetString(objv[3]);
1794        g_renderer->setGlyphsEdgeVisibility(name, state);
1795    } else {
1796        g_renderer->setGlyphsEdgeVisibility("all", state);
1797    }
1798    return TCL_OK;
1799}
1800
1801static int
1802GlyphsLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
1803                 Tcl_Obj *const *objv)
1804{
1805    bool state;
1806    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
1807        return TCL_ERROR;
1808    }
1809    if (objc == 4) {
1810        const char *name = Tcl_GetString(objv[3]);
1811        g_renderer->setGlyphsLighting(name, state);
1812    } else {
1813        g_renderer->setGlyphsLighting("all", state);
1814    }
1815    return TCL_OK;
1816}
1817
1818static int
1819GlyphsLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1820                  Tcl_Obj *const *objv)
1821{
1822    float color[3];
1823    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
1824        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
1825        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
1826        return TCL_ERROR;
1827    }
1828    if (objc == 6) {
1829        const char *name = Tcl_GetString(objv[5]);
1830        g_renderer->setGlyphsEdgeColor(name, color);
1831    } else {
1832        g_renderer->setGlyphsEdgeColor("all", color);
1833    }
1834    return TCL_OK;
1835}
1836
1837static int
1838GlyphsLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
1839                  Tcl_Obj *const *objv)
1840{
1841    float width;
1842    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
1843        return TCL_ERROR;
1844    }
1845    if (objc == 4) {
1846        const char *name = Tcl_GetString(objv[3]);
1847        g_renderer->setGlyphsEdgeWidth(name, width);
1848    } else {
1849        g_renderer->setGlyphsEdgeWidth("all", width);
1850    }
1851    return TCL_OK;
1852}
1853
1854static int
1855GlyphsNormalizeScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1856                       Tcl_Obj *const *objv)
1857{
1858    bool normalize;
1859    if (GetBooleanFromObj(interp, objv[2], &normalize) != TCL_OK) {
1860        return TCL_ERROR;
1861    }
1862    if (objc == 4) {
1863        const char *name = Tcl_GetString(objv[3]);
1864        g_renderer->setGlyphsNormalizeScale(name, normalize);
1865    } else {
1866        g_renderer->setGlyphsNormalizeScale("all", normalize);
1867    }
1868    return TCL_OK;
1869}
1870
1871static int
1872GlyphsOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
1873                Tcl_Obj *const *objv)
1874{
1875    double opacity;
1876    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
1877        return TCL_ERROR;
1878    }
1879    if (objc == 4) {
1880        const char *name = Tcl_GetString(objv[3]);
1881        g_renderer->setGlyphsOpacity(name, opacity);
1882    } else {
1883        g_renderer->setGlyphsOpacity("all", opacity);
1884    }
1885    return TCL_OK;
1886}
1887
1888static int
1889GlyphsOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
1890               Tcl_Obj *const *objv)
1891{
1892    double quat[4];
1893    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
1894        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
1895        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
1896        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
1897        return TCL_ERROR;
1898    }
1899    if (objc == 7) {
1900        const char *name = Tcl_GetString(objv[6]);
1901        g_renderer->setGlyphsOrientation(name, quat);
1902    } else {
1903        g_renderer->setGlyphsOrientation("all", quat);
1904    }
1905    return TCL_OK;
1906}
1907
1908static int
1909GlyphsPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
1910                 Tcl_Obj *const *objv)
1911{
1912    double pos[3];
1913    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
1914        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
1915        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
1916        return TCL_ERROR;
1917    }
1918    if (objc == 6) {
1919        const char *name = Tcl_GetString(objv[5]);
1920        g_renderer->setGlyphsPosition(name, pos);
1921    } else {
1922        g_renderer->setGlyphsPosition("all", pos);
1923    }
1924    return TCL_OK;
1925}
1926
1927static int
1928GlyphsScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
1929              Tcl_Obj *const *objv)
1930{
1931    double scale[3];
1932    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
1933        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
1934        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
1935        return TCL_ERROR;
1936    }
1937    if (objc == 6) {
1938        const char *name = Tcl_GetString(objv[5]);
1939        g_renderer->setGlyphsScale(name, scale);
1940    } else {
1941        g_renderer->setGlyphsScale("all", scale);
1942    }
1943    return TCL_OK;
1944}
1945
1946static int
1947GlyphsScaleFactorOp(ClientData clientData, Tcl_Interp *interp, int objc,
1948                    Tcl_Obj *const *objv)
1949{
1950    double scale;
1951    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale) != TCL_OK) {
1952        return TCL_ERROR;
1953    }
1954    if (objc == 4) {
1955        const char *name = Tcl_GetString(objv[3]);
1956        g_renderer->setGlyphsScaleFactor(name, scale);
1957    } else {
1958        g_renderer->setGlyphsScaleFactor("all", scale);
1959    }
1960    return TCL_OK;
1961}
1962
1963static int
1964GlyphsScalingModeOp(ClientData clientData, Tcl_Interp *interp, int objc,
1965                    Tcl_Obj *const *objv)
1966{
1967    Glyphs::ScalingMode mode;
1968    const char *str = Tcl_GetString(objv[2]);
1969    if (str[0] == 's' && strcmp(str, "scalar") == 0) {
1970        mode = Glyphs::SCALE_BY_SCALAR;
1971    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
1972        mode = Glyphs::SCALE_BY_VECTOR_MAGNITUDE;
1973    } else if (str[0] == 'v' && strcmp(str, "vcomp") == 0) {
1974        mode = Glyphs::SCALE_BY_VECTOR_COMPONENTS;
1975    } else if (str[0] == 'o' && strcmp(str, "off") == 0) {
1976        mode = Glyphs::SCALING_OFF;
1977    } else {
1978        Tcl_AppendResult(interp, "bad scaling mode option \"", str,
1979                         "\": should be one of: 'scalar', 'vmag', 'vcomp', 'off'", (char*)NULL);
1980        return TCL_ERROR;
1981    }
1982    if (objc == 4) {
1983        const char *name = Tcl_GetString(objv[3]);
1984        g_renderer->setGlyphsScalingMode(name, mode);
1985    } else {
1986        g_renderer->setGlyphsScalingMode("all", mode);
1987    }
1988    return TCL_OK;
1989}
1990
1991static int
1992GlyphsShapeOp(ClientData clientData, Tcl_Interp *interp, int objc,
1993              Tcl_Obj *const *objv)
1994{
1995    Glyphs::GlyphShape shape;
1996
1997    const char *shapeOpt = Tcl_GetString(objv[2]);
1998    if (shapeOpt[0] == 'a' && strcmp(shapeOpt, "arrow") == 0) {
1999        shape = Glyphs::ARROW;
2000    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cone") == 0) {
2001        shape = Glyphs::CONE;
2002    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cube") == 0) {
2003        shape = Glyphs::CUBE;
2004    } else if (shapeOpt[0] == 'c' && strcmp(shapeOpt, "cylinder") == 0) {
2005        shape = Glyphs::CYLINDER;
2006    } else if (shapeOpt[0] == 'd' && strcmp(shapeOpt, "dodecahedron") == 0) {
2007        shape = Glyphs::DODECAHEDRON;
2008    } else if (shapeOpt[0] == 'i' && strcmp(shapeOpt, "icosahedron") == 0) {
2009        shape = Glyphs::ICOSAHEDRON;
2010    } else if (shapeOpt[0] == 'l' && strcmp(shapeOpt, "line") == 0) {
2011        shape = Glyphs::LINE;
2012    } else if (shapeOpt[0] == 'o' && strcmp(shapeOpt, "octahedron") == 0) {
2013        shape = Glyphs::OCTAHEDRON;
2014    } else if (shapeOpt[0] == 's' && strcmp(shapeOpt, "sphere") == 0) {
2015        shape = Glyphs::SPHERE;
2016    } else if (shapeOpt[0] == 't' && strcmp(shapeOpt, "tetrahedron") == 0) {
2017        shape = Glyphs::TETRAHEDRON;
2018    } else {
2019        Tcl_AppendResult(interp, "bad shape option \"", shapeOpt,
2020                         "\": should be one of: 'arrow', 'cone', 'cube', 'cylinder', 'dodecahedron', 'icosahedron', 'line', 'octahedron', 'sphere', 'tetrahedron'", (char*)NULL);
2021        return TCL_ERROR;
2022    }
2023
2024    if (objc == 4) {
2025        const char *name = Tcl_GetString(objv[3]);
2026        g_renderer->setGlyphsShape(name, shape);
2027    } else {
2028        g_renderer->setGlyphsShape("all", shape);
2029    }
2030    return TCL_OK;
2031}
2032
2033static int
2034GlyphsVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2035                Tcl_Obj *const *objv)
2036{
2037    bool state;
2038    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2039        return TCL_ERROR;
2040    }
2041    if (objc == 4) {
2042        const char *name = Tcl_GetString(objv[3]);
2043        g_renderer->setGlyphsVisibility(name, state);
2044    } else {
2045        g_renderer->setGlyphsVisibility("all", state);
2046    }
2047    return TCL_OK;
2048}
2049
2050static int
2051GlyphsWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc,
2052                  Tcl_Obj *const *objv)
2053{
2054    bool state;
2055    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2056        return TCL_ERROR;
2057    }
2058    if (objc == 4) {
2059        const char *name = Tcl_GetString(objv[3]);
2060        g_renderer->setGlyphsWireframe(name, state);
2061    } else {
2062        g_renderer->setGlyphsWireframe("all", state);
2063    }
2064    return TCL_OK;
2065}
2066
2067static Rappture::CmdSpec glyphsOps[] = {
2068    {"add",       1, GlyphsAddOp, 3, 4, "shape ?dataSetNme?"},
2069    {"ccolor",    2, GlyphsColorOp, 5, 6, "r g b ?dataSetName?"},
2070    {"colormap",  7, GlyphsColorMapOp, 3, 4, "colorMapName ?dataSetNme?"},
2071    {"colormode", 7, GlyphsColorModeOp, 3, 4, "mode ?dataSetNme?"},
2072    {"delete",    1, GlyphsDeleteOp, 2, 3, "?dataSetName?"},
2073    {"edges",     1, GlyphsEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
2074    {"gscale",    1, GlyphsScaleFactorOp, 3, 4, "scaleFactor ?dataSetName?"},
2075    {"lighting",  3, GlyphsLightingOp, 3, 4, "bool ?dataSetName?"},
2076    {"linecolor", 5, GlyphsLineColorOp, 5, 6, "r g b ?dataSetName?"},
2077    {"linewidth", 5, GlyphsLineWidthOp, 3, 4, "width ?dataSetName?"},
2078    {"normscale", 1, GlyphsNormalizeScaleOp, 3, 4, "bool ?dataSetName?"},
2079    {"opacity",   2, GlyphsOpacityOp, 3, 4, "value ?dataSetName?"},
2080    {"orient",    2, GlyphsOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
2081    {"pos",       1, GlyphsPositionOp, 5, 6, "x y z ?dataSetName?"},
2082    {"scale",     2, GlyphsScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
2083    {"shape",     2, GlyphsShapeOp, 3, 4, "shapeVal ?dataSetName?"},
2084    {"smode",     2, GlyphsScalingModeOp, 3, 4, "mode ?dataSetNme?"},
2085    {"visible",   1, GlyphsVisibleOp, 3, 4, "bool ?dataSetName?"},
2086    {"wireframe", 1, GlyphsWireframeOp, 3, 4, "bool ?dataSetName?"}
2087};
2088static int nGlyphsOps = NumCmdSpecs(glyphsOps);
2089
2090static int
2091GlyphsCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2092          Tcl_Obj *const *objv)
2093{
2094    Tcl_ObjCmdProc *proc;
2095
2096    proc = Rappture::GetOpFromObj(interp, nGlyphsOps, glyphsOps,
2097                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2098    if (proc == NULL) {
2099        return TCL_ERROR;
2100    }
2101    return (*proc) (clientData, interp, objc, objv);
2102}
2103
2104static int
2105HeightMapAddContourListOp(ClientData clientData, Tcl_Interp *interp, int objc,
2106                          Tcl_Obj *const *objv)
2107{
2108    std::vector<double> contourList;
2109    double heightScale;
2110
2111    int clistc;
2112    Tcl_Obj **clistv;
2113
2114    if (Tcl_ListObjGetElements(interp, objv[3], &clistc, &clistv) != TCL_OK) {
2115        return TCL_ERROR;
2116    }
2117
2118    for (int i = 0; i < clistc; i++) {
2119        double val;
2120        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
2121            return TCL_ERROR;
2122        }
2123        contourList.push_back(val);
2124    }
2125
2126    if (Tcl_GetDoubleFromObj(interp, objv[4], &heightScale) != TCL_OK) {
2127        return TCL_ERROR;
2128    }
2129
2130    if (objc == 6) {
2131        const char *name = Tcl_GetString(objv[5]);
2132        if (!g_renderer->addHeightMap(name, contourList, heightScale)) {
2133            Tcl_AppendResult(interp, "Failed to create heightmap", (char*)NULL);
2134            return TCL_ERROR;
2135        }
2136    } else {
2137        if (!g_renderer->addHeightMap("all", contourList, heightScale)) {
2138            Tcl_AppendResult(interp, "Failed to create heightmap for one or more data sets", (char*)NULL);
2139            return TCL_ERROR;
2140        }
2141    }
2142    return TCL_OK;
2143}
2144
2145static int
2146HeightMapAddNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc,
2147                          Tcl_Obj *const *objv)
2148{
2149    int numContours;
2150    double heightScale;
2151    if (Tcl_GetIntFromObj(interp, objv[3], &numContours) != TCL_OK) {
2152        return TCL_ERROR;
2153    }
2154    if (Tcl_GetDoubleFromObj(interp, objv[4], &heightScale) != TCL_OK) {
2155        return TCL_ERROR;
2156    }
2157    if (objc == 6) {
2158        const char *name = Tcl_GetString(objv[5]);
2159        if (!g_renderer->addHeightMap(name, numContours, heightScale)) {
2160            Tcl_AppendResult(interp, "Failed to create heightmap", (char*)NULL);
2161            return TCL_ERROR;
2162        }
2163    } else {
2164        if (!g_renderer->addHeightMap("all", numContours, heightScale)) {
2165            Tcl_AppendResult(interp, "Failed to create heightmap for one or more data sets", (char*)NULL);
2166            return TCL_ERROR;
2167        }
2168    }
2169    return TCL_OK;
2170}
2171
2172static Rappture::CmdSpec heightmapAddOps[] = {
2173    {"contourlist", 1, HeightMapAddContourListOp, 5, 6, "contourList heightscale ?dataSetName?"},
2174    {"numcontours", 1, HeightMapAddNumContoursOp, 5, 6, "numContours heightscale ?dataSetName?"}
2175};
2176static int nHeightmapAddOps = NumCmdSpecs(heightmapAddOps);
2177
2178static int
2179HeightMapAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
2180               Tcl_Obj *const *objv)
2181{
2182    Tcl_ObjCmdProc *proc;
2183
2184    proc = Rappture::GetOpFromObj(interp, nHeightmapAddOps, heightmapAddOps,
2185                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
2186    if (proc == NULL) {
2187        return TCL_ERROR;
2188    }
2189    return (*proc) (clientData, interp, objc, objv);
2190}
2191
2192static int
2193HeightMapColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
2194                    Tcl_Obj *const *objv)
2195{
2196    const char *colorMapName = Tcl_GetString(objv[2]);
2197    if (objc == 4) {
2198        const char *dataSetName = Tcl_GetString(objv[3]);
2199        g_renderer->setHeightMapColorMap(dataSetName, colorMapName);
2200    } else {
2201        g_renderer->setHeightMapColorMap("all", colorMapName);
2202    }
2203    return TCL_OK;
2204}
2205
2206static int
2207HeightMapContourLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2208                            Tcl_Obj *const *objv)
2209{
2210    float color[3];
2211    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
2212        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
2213        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
2214        return TCL_ERROR;
2215    }
2216    if (objc == 6) {
2217        const char *name = Tcl_GetString(objv[5]);
2218        g_renderer->setHeightMapContourEdgeColor(name, color);
2219    } else {
2220        g_renderer->setHeightMapContourEdgeColor("all", color);
2221    }
2222    return TCL_OK;
2223}
2224
2225static int
2226HeightMapContourLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
2227                            Tcl_Obj *const *objv)
2228{
2229    float width;
2230    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
2231        return TCL_ERROR;
2232    }
2233    if (objc == 4) {
2234        const char *name = Tcl_GetString(objv[3]);
2235        g_renderer->setHeightMapContourEdgeWidth(name, width);
2236    } else {
2237        g_renderer->setHeightMapContourEdgeWidth("all", width);
2238    }
2239    return TCL_OK;
2240}
2241
2242static int
2243HeightMapContourListOp(ClientData clientData, Tcl_Interp *interp, int objc,
2244                       Tcl_Obj *const *objv)
2245{
2246    std::vector<double> contourList;
2247
2248    int clistc;
2249    Tcl_Obj **clistv;
2250
2251    if (Tcl_ListObjGetElements(interp, objv[2], &clistc, &clistv) != TCL_OK) {
2252        return TCL_ERROR;
2253    }
2254
2255    for (int i = 0; i < clistc; i++) {
2256        double val;
2257        if (Tcl_GetDoubleFromObj(interp, clistv[i], &val) != TCL_OK) {
2258            return TCL_ERROR;
2259        }
2260        contourList.push_back(val);
2261    }
2262
2263    if (objc == 4) {
2264        const char *name = Tcl_GetString(objv[3]);
2265        g_renderer->setHeightMapContourList(name, contourList);
2266    } else {
2267        g_renderer->setHeightMapContourList("all", contourList);
2268    }
2269    return TCL_OK;
2270}
2271
2272static int
2273HeightMapContourLineVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2274                              Tcl_Obj *const *objv)
2275{
2276    bool state;
2277    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2278        return TCL_ERROR;
2279    }
2280    if (objc == 4) {
2281        const char *name = Tcl_GetString(objv[3]);
2282        g_renderer->setHeightMapContourLineVisibility(name, state);
2283    } else {
2284        g_renderer->setHeightMapContourLineVisibility("all", state);
2285    }
2286    return TCL_OK;
2287}
2288
2289static int
2290HeightMapContourSurfaceVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2291                                 Tcl_Obj *const *objv)
2292{
2293    bool state;
2294    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2295        return TCL_ERROR;
2296    }
2297    if (objc == 4) {
2298        const char *name = Tcl_GetString(objv[3]);
2299        g_renderer->setHeightMapContourSurfaceVisibility(name, state);
2300    } else {
2301        g_renderer->setHeightMapContourSurfaceVisibility("all", state);
2302    }
2303    return TCL_OK;
2304}
2305
2306static int
2307HeightMapDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
2308                  Tcl_Obj *const *objv)
2309{
2310    if (objc == 3) {
2311        const char *name = Tcl_GetString(objv[2]);
2312        g_renderer->deleteHeightMap(name);
2313    } else {
2314        g_renderer->deleteHeightMap("all");
2315    }
2316    return TCL_OK;
2317}
2318
2319static int
2320HeightMapEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
2321                          Tcl_Obj *const *objv)
2322{
2323    bool state;
2324    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2325        return TCL_ERROR;
2326    }
2327    if (objc == 4) {
2328        const char *name = Tcl_GetString(objv[3]);
2329        g_renderer->setHeightMapEdgeVisibility(name, state);
2330    } else {
2331        g_renderer->setHeightMapEdgeVisibility("all", state);
2332    }
2333    return TCL_OK;
2334}
2335
2336static int
2337HeightMapHeightScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2338                       Tcl_Obj *const *objv)
2339{
2340    double scale;
2341    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale) != TCL_OK) {
2342        return TCL_ERROR;
2343    }
2344    if (objc == 4) {
2345        const char *name = Tcl_GetString(objv[3]);
2346        g_renderer->setHeightMapHeightScale(name, scale);
2347    } else {
2348        g_renderer->setHeightMapHeightScale("all", scale);
2349    }
2350    return TCL_OK;
2351}
2352
2353static int
2354HeightMapLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
2355                    Tcl_Obj *const *objv)
2356{
2357    bool state;
2358    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2359        return TCL_ERROR;
2360    }
2361    if (objc == 4) {
2362        const char *name = Tcl_GetString(objv[3]);
2363        g_renderer->setHeightMapLighting(name, state);
2364    } else {
2365        g_renderer->setHeightMapLighting("all", state);
2366    }
2367    return TCL_OK;
2368}
2369
2370static int
2371HeightMapLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2372                     Tcl_Obj *const *objv)
2373{
2374    float color[3];
2375    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
2376        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
2377        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
2378        return TCL_ERROR;
2379    }
2380    if (objc == 6) {
2381        const char *name = Tcl_GetString(objv[5]);
2382        g_renderer->setHeightMapEdgeColor(name, color);
2383    } else {
2384        g_renderer->setHeightMapEdgeColor("all", color);
2385    }
2386    return TCL_OK;
2387}
2388
2389static int
2390HeightMapLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
2391                     Tcl_Obj *const *objv)
2392{
2393    float width;
2394    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
2395        return TCL_ERROR;
2396    }
2397    if (objc == 4) {
2398        const char *name = Tcl_GetString(objv[3]);
2399        g_renderer->setHeightMapEdgeWidth(name, width);
2400    } else {
2401        g_renderer->setHeightMapEdgeWidth("all", width);
2402    }
2403    return TCL_OK;
2404}
2405
2406static int
2407HeightMapNumContoursOp(ClientData clientData, Tcl_Interp *interp, int objc,
2408                       Tcl_Obj *const *objv)
2409{
2410    int numContours;
2411
2412    if (Tcl_GetIntFromObj(interp, objv[2], &numContours) != TCL_OK) {
2413        return TCL_ERROR;
2414    }
2415    if (objc == 4) {
2416        const char *name = Tcl_GetString(objv[3]);
2417        g_renderer->setHeightMapNumContours(name, numContours);
2418    } else {
2419        g_renderer->setHeightMapNumContours("all", numContours);
2420    }
2421    return TCL_OK;
2422}
2423
2424static int
2425HeightMapOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
2426                   Tcl_Obj *const *objv)
2427{
2428    double opacity;
2429    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
2430        return TCL_ERROR;
2431    }
2432    if (objc == 4) {
2433        const char *name = Tcl_GetString(objv[3]);
2434        g_renderer->setHeightMapOpacity(name, opacity);
2435    } else {
2436        g_renderer->setHeightMapOpacity("all", opacity);
2437    }
2438    return TCL_OK;
2439}
2440
2441static int
2442HeightMapOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
2443                  Tcl_Obj *const *objv)
2444{
2445    double quat[4];
2446    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
2447        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
2448        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
2449        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
2450        return TCL_ERROR;
2451    }
2452    if (objc == 7) {
2453        const char *name = Tcl_GetString(objv[6]);
2454        g_renderer->setHeightMapOrientation(name, quat);
2455    } else {
2456        g_renderer->setHeightMapOrientation("all", quat);
2457    }
2458    return TCL_OK;
2459}
2460
2461static int
2462HeightMapPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
2463                    Tcl_Obj *const *objv)
2464{
2465    double pos[3];
2466    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
2467        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
2468        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
2469        return TCL_ERROR;
2470    }
2471    if (objc == 6) {
2472        const char *name = Tcl_GetString(objv[5]);
2473        g_renderer->setHeightMapPosition(name, pos);
2474    } else {
2475        g_renderer->setHeightMapPosition("all", pos);
2476    }
2477    return TCL_OK;
2478}
2479
2480static int
2481HeightMapScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2482                 Tcl_Obj *const *objv)
2483{
2484    double scale[3];
2485    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
2486        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
2487        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
2488        return TCL_ERROR;
2489    }
2490    if (objc == 6) {
2491        const char *name = Tcl_GetString(objv[5]);
2492        g_renderer->setHeightMapScale(name, scale);
2493    } else {
2494        g_renderer->setHeightMapScale("all", scale);
2495    }
2496    return TCL_OK;
2497}
2498
2499static int
2500HeightMapVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2501                   Tcl_Obj *const *objv)
2502{
2503    bool state;
2504    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2505        return TCL_ERROR;
2506    }
2507    if (objc == 4) {
2508        const char *name = Tcl_GetString(objv[3]);
2509        g_renderer->setHeightMapVisibility(name, state);
2510    } else {
2511        g_renderer->setHeightMapVisibility("all", state);
2512    }
2513    return TCL_OK;
2514}
2515
2516static int
2517HeightMapVolumeSliceOp(ClientData clientData, Tcl_Interp *interp, int objc,
2518                       Tcl_Obj *const *objv)
2519{
2520    double ratio;
2521    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
2522        return TCL_ERROR;
2523    }
2524    const char *string = Tcl_GetString(objv[2]);
2525    char c = string[0];
2526    HeightMap::Axis axis;
2527    if ((c == 'x') && (strcmp(string, "x") == 0)) {
2528        axis = HeightMap::X_AXIS;
2529    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
2530        axis = HeightMap::Y_AXIS;
2531    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
2532        axis = HeightMap::Z_AXIS;
2533    } else {
2534        Tcl_AppendResult(interp, "bad axis option \"", string,
2535                         "\": should be axisName ratio", (char*)NULL);
2536        return TCL_ERROR;
2537    }
2538    if (objc == 5) {
2539        const char *name = Tcl_GetString(objv[4]);
2540        g_renderer->setHeightMapVolumeSlice(name, axis, ratio);
2541    } else {
2542        g_renderer->setHeightMapVolumeSlice("all", axis, ratio);
2543    }
2544    return TCL_OK;
2545}
2546
2547static int
2548HeightMapWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc,
2549                     Tcl_Obj *const *objv)
2550{
2551    bool state;
2552    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2553        return TCL_ERROR;
2554    }
2555    if (objc == 4) {
2556        const char *name = Tcl_GetString(objv[3]);
2557        g_renderer->setHeightMapWireframe(name, state);
2558    } else {
2559        g_renderer->setHeightMapWireframe("all", state);
2560    }
2561    return TCL_OK;
2562}
2563
2564static Rappture::CmdSpec heightmapOps[] = {
2565    {"add",          1, HeightMapAddOp, 5, 6, "oper value ?dataSetName?"},
2566    {"colormap",     2, HeightMapColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
2567    {"contourlist",  2, HeightMapContourListOp, 3, 4, "contourList ?dataSetName?"},
2568    {"delete",       1, HeightMapDeleteOp, 2, 3, "?dataSetName?"},
2569    {"edges",        1, HeightMapEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
2570    {"heightscale",  1, HeightMapHeightScaleOp, 3, 4, "value ?dataSetName?"},
2571    {"isolinecolor", 8, HeightMapContourLineColorOp, 5, 6, "r g b ?dataSetName?"},
2572    {"isolines",     8, HeightMapContourLineVisibleOp, 3, 4, "bool ?dataSetName?"},
2573    {"isolinewidth", 8, HeightMapContourLineWidthOp, 3, 4, "width ?dataSetName?"},
2574    {"lighting",     3, HeightMapLightingOp, 3, 4, "bool ?dataSetName?"},
2575    {"linecolor",    5, HeightMapLineColorOp, 5, 6, "r g b ?dataSetName?"},
2576    {"linewidth",    5, HeightMapLineWidthOp, 3, 4, "width ?dataSetName?"},
2577    {"numcontours",  1, HeightMapNumContoursOp, 3, 4, "numContours ?dataSetName?"},
2578    {"opacity",      2, HeightMapOpacityOp, 3, 4, "value ?dataSetName?"},
2579    {"orient",       2, HeightMapOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
2580    {"pos",          1, HeightMapPositionOp, 5, 6, "x y z ?dataSetName?"},
2581    {"scale",        1, HeightMapScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
2582    {"surface",      2, HeightMapContourSurfaceVisibleOp, 3, 4, "bool ?dataSetName?"},
2583    {"visible",      2, HeightMapVisibleOp, 3, 4, "bool ?dataSetName?"},
2584    {"volumeslice",  2, HeightMapVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"},
2585    {"wireframe",    1, HeightMapWireframeOp, 3, 4, "bool ?dataSetName?"}
2586};
2587static int nHeightmapOps = NumCmdSpecs(heightmapOps);
2588
2589static int
2590HeightMapCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2591             Tcl_Obj *const *objv)
2592{
2593    Tcl_ObjCmdProc *proc;
2594
2595    proc = Rappture::GetOpFromObj(interp, nHeightmapOps, heightmapOps,
2596                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2597    if (proc == NULL) {
2598        return TCL_ERROR;
2599    }
2600    return (*proc) (clientData, interp, objc, objv);
2601}
2602
2603static int
2604LegendCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2605          Tcl_Obj *const *objv)
2606{
2607    if (objc < 7) {
2608        Tcl_AppendResult(interp, "wrong # args: should be \"",
2609                Tcl_GetString(objv[0]), " colormapName legendType title width height numLabels ?dataSetName?\"", (char*)NULL);
2610        return TCL_ERROR;
2611    }
2612    const char *colorMapName = Tcl_GetString(objv[1]);
2613    const char *typeStr = Tcl_GetString(objv[2]);
2614    Renderer::LegendType type;
2615    if (typeStr[0] == 's' && strcmp(typeStr, "scalar") == 0) {
2616        type = Renderer::ACTIVE_SCALAR;
2617    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vmag") == 0) {
2618        type = Renderer::ACTIVE_VECTOR_MAGNITUDE;
2619    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vx") == 0) {
2620        type = Renderer::ACTIVE_VECTOR_X;
2621    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vy") == 0) {
2622        type = Renderer::ACTIVE_VECTOR_Y;
2623    } else if (typeStr[0] == 'v' && strcmp(typeStr, "vz") == 0) {
2624        type = Renderer::ACTIVE_VECTOR_Z;
2625    } else {
2626        Tcl_AppendResult(interp, "Bad legendType option \"",
2627                         typeStr, "\", should be one of 'scalar', 'vmag', 'vx', 'vy', 'vz'", (char*)NULL);
2628        return TCL_ERROR;
2629    }
2630
2631    std::string title(Tcl_GetString(objv[3]));
2632    int width, height, numLabels;
2633
2634    if (Tcl_GetIntFromObj(interp, objv[4], &width) != TCL_OK ||
2635        Tcl_GetIntFromObj(interp, objv[5], &height) != TCL_OK ||
2636        Tcl_GetIntFromObj(interp, objv[6], &numLabels) != TCL_OK) {
2637        return TCL_ERROR;
2638    }
2639
2640    vtkSmartPointer<vtkUnsignedCharArray> imgData =
2641        vtkSmartPointer<vtkUnsignedCharArray>::New();
2642
2643    double range[2];
2644
2645    if (objc == 8) {
2646        const char *dataSetName = Tcl_GetString(objv[7]);
2647        if (!g_renderer->renderColorMap(colorMapName, dataSetName, type, title,
2648                                        range, width, height, numLabels, imgData)) {
2649            Tcl_AppendResult(interp, "Color map \"",
2650                             colorMapName, "\" or dataset \"",
2651                             dataSetName, "\" was not found", (char*)NULL);
2652            return TCL_ERROR;
2653        }
2654    } else {
2655        if (!g_renderer->renderColorMap(colorMapName, "all", type, title,
2656                                        range, width, height, numLabels, imgData)) {
2657            Tcl_AppendResult(interp, "Color map \"",
2658                             colorMapName, "\" was not found", (char*)NULL);
2659            return TCL_ERROR;
2660        }
2661    }
2662
2663#ifdef DEBUG
2664    writeTGAFile("/tmp/legend.tga", imgData->GetPointer(0), width, height,
2665                 TARGA_BYTES_PER_PIXEL);
2666#else
2667    char cmd[256];
2668    snprintf(cmd, sizeof(cmd), "nv>legend {%s} {%s} %g %g",
2669             colorMapName, title.c_str(), range[0], range[1]);
2670#ifdef RENDER_TARGA
2671    writeTGA(g_fdOut, cmd, imgData->GetPointer(0), width, height,
2672                 TARGA_BYTES_PER_PIXEL);
2673#else
2674    writePPM(g_fdOut, cmd, imgData->GetPointer(0), width, height);
2675#endif
2676#endif
2677
2678    return TCL_OK;
2679}
2680
2681static int
2682LICAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
2683         Tcl_Obj *const *objv)
2684{
2685    if (objc == 3) {
2686        const char *name = Tcl_GetString(objv[2]);
2687        if (!g_renderer->addLIC(name)) {
2688            Tcl_AppendResult(interp, "Failed to create lic", (char*)NULL);
2689            return TCL_ERROR;
2690        }
2691    } else {
2692        if (!g_renderer->addLIC("all")) {
2693            Tcl_AppendResult(interp, "Failed to create lic for one or more data sets", (char*)NULL);
2694            return TCL_ERROR;
2695        }
2696    }
2697    return TCL_OK;
2698}
2699
2700static int
2701LICColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
2702              Tcl_Obj *const *objv)
2703{
2704    const char *colorMapName = Tcl_GetString(objv[2]);
2705    if (objc == 4) {
2706        const char *dataSetName = Tcl_GetString(objv[3]);
2707        g_renderer->setLICColorMap(dataSetName, colorMapName);
2708    } else {
2709        g_renderer->setLICColorMap("all", colorMapName);
2710    }
2711    return TCL_OK;
2712}
2713
2714static int
2715LICDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
2716            Tcl_Obj *const *objv)
2717{
2718    if (objc == 3) {
2719        const char *name = Tcl_GetString(objv[2]);
2720        g_renderer->deleteLIC(name);
2721    } else {
2722        g_renderer->deleteLIC("all");
2723    }
2724    return TCL_OK;
2725}
2726
2727static int
2728LICEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
2729                    Tcl_Obj *const *objv)
2730{
2731    bool state;
2732    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2733        return TCL_ERROR;
2734    }
2735    if (objc == 4) {
2736        const char *name = Tcl_GetString(objv[3]);
2737        g_renderer->setLICEdgeVisibility(name, state);
2738    } else {
2739        g_renderer->setLICEdgeVisibility("all", state);
2740    }
2741    return TCL_OK;
2742}
2743
2744static int
2745LICLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
2746              Tcl_Obj *const *objv)
2747{
2748    bool state;
2749    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2750        return TCL_ERROR;
2751    }
2752    if (objc == 4) {
2753        const char *name = Tcl_GetString(objv[3]);
2754        g_renderer->setLICLighting(name, state);
2755    } else {
2756        g_renderer->setLICLighting("all", state);
2757    }
2758    return TCL_OK;
2759}
2760
2761static int
2762LICLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
2763               Tcl_Obj *const *objv)
2764{
2765    float color[3];
2766    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
2767        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
2768        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
2769        return TCL_ERROR;
2770    }
2771    if (objc == 6) {
2772        const char *name = Tcl_GetString(objv[5]);
2773        g_renderer->setLICEdgeColor(name, color);
2774    } else {
2775        g_renderer->setLICEdgeColor("all", color);
2776    }
2777    return TCL_OK;
2778}
2779
2780static int
2781LICLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
2782               Tcl_Obj *const *objv)
2783{
2784    float width;
2785    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
2786        return TCL_ERROR;
2787    }
2788    if (objc == 4) {
2789        const char *name = Tcl_GetString(objv[3]);
2790        g_renderer->setLICEdgeWidth(name, width);
2791    } else {
2792        g_renderer->setLICEdgeWidth("all", width);
2793    }
2794    return TCL_OK;
2795}
2796
2797static int
2798LICOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
2799             Tcl_Obj *const *objv)
2800{
2801    double opacity;
2802    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
2803        return TCL_ERROR;
2804    }
2805    if (objc == 4) {
2806        const char *name = Tcl_GetString(objv[3]);
2807        g_renderer->setLICOpacity(name, opacity);
2808    } else {
2809        g_renderer->setLICOpacity("all", opacity);
2810    }
2811    return TCL_OK;
2812}
2813
2814static int
2815LICOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
2816            Tcl_Obj *const *objv)
2817{
2818    double quat[4];
2819    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
2820        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
2821        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
2822        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
2823        return TCL_ERROR;
2824    }
2825    if (objc == 7) {
2826        const char *name = Tcl_GetString(objv[6]);
2827        g_renderer->setLICOrientation(name, quat);
2828    } else {
2829        g_renderer->setLICOrientation("all", quat);
2830    }
2831    return TCL_OK;
2832}
2833
2834static int
2835LICPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
2836              Tcl_Obj *const *objv)
2837{
2838    double pos[3];
2839    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
2840        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
2841        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
2842        return TCL_ERROR;
2843    }
2844    if (objc == 6) {
2845        const char *name = Tcl_GetString(objv[5]);
2846        g_renderer->setLICPosition(name, pos);
2847    } else {
2848        g_renderer->setLICPosition("all", pos);
2849    }
2850    return TCL_OK;
2851}
2852
2853static int
2854LICScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2855           Tcl_Obj *const *objv)
2856{
2857    double scale[3];
2858    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
2859        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
2860        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
2861        return TCL_ERROR;
2862    }
2863    if (objc == 6) {
2864        const char *name = Tcl_GetString(objv[5]);
2865        g_renderer->setLICScale(name, scale);
2866    } else {
2867        g_renderer->setLICScale("all", scale);
2868    }
2869    return TCL_OK;
2870}
2871
2872static int
2873LICVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
2874             Tcl_Obj *const *objv)
2875{
2876    bool state;
2877    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2878        return TCL_ERROR;
2879    }
2880    if (objc == 4) {
2881        const char *name = Tcl_GetString(objv[3]);
2882        g_renderer->setLICVisibility(name, state);
2883    } else {
2884        g_renderer->setLICVisibility("all", state);
2885    }
2886    return TCL_OK;
2887}
2888
2889static int
2890LICVolumeSliceOp(ClientData clientData, Tcl_Interp *interp, int objc,
2891                 Tcl_Obj *const *objv)
2892{
2893    double ratio;
2894    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
2895        return TCL_ERROR;
2896    }
2897    const char *string = Tcl_GetString(objv[2]);
2898    char c = string[0];
2899    LIC::Axis axis;
2900    if ((c == 'x') && (strcmp(string, "x") == 0)) {
2901        axis = LIC::X_AXIS;
2902    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
2903        axis = LIC::Y_AXIS;
2904    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
2905        axis = LIC::Z_AXIS;
2906    } else {
2907        Tcl_AppendResult(interp, "bad axis option \"", string,
2908                         "\": should be axisName ratio", (char*)NULL);
2909        return TCL_ERROR;
2910    }
2911    if (objc == 5) {
2912        const char *name = Tcl_GetString(objv[4]);
2913        g_renderer->setLICVolumeSlice(name, axis, ratio);
2914    } else {
2915        g_renderer->setLICVolumeSlice("all", axis, ratio);
2916    }
2917    return TCL_OK;
2918}
2919
2920static Rappture::CmdSpec licOps[] = {
2921    {"add",         1, LICAddOp, 2, 3, "?dataSetName?"},
2922    {"colormap",    1, LICColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
2923    {"delete",      1, LICDeleteOp, 2, 3, "?dataSetName?"},
2924    {"edges",       1, LICEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
2925    {"lighting",    3, LICLightingOp, 3, 4, "bool ?dataSetName?"},
2926    {"linecolor",   5, LICLineColorOp, 5, 6, "r g b ?dataSetName?"},
2927    {"linewidth",   5, LICLineWidthOp, 3, 4, "width ?dataSetName?"},
2928    {"opacity",     2, LICOpacityOp, 3, 4, "value ?dataSetName?"},
2929    {"orient",      2, LICOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
2930    {"pos",         1, LICPositionOp, 5, 6, "x y z ?dataSetName?"},
2931    {"scale",       1, LICScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
2932    {"visible",     2, LICVisibleOp, 3, 4, "bool ?dataSetName?"},
2933    {"volumeslice", 2, LICVolumeSliceOp, 4, 5, "axis ratio ?dataSetName?"}
2934};
2935static int nLICOps = NumCmdSpecs(licOps);
2936
2937static int
2938LICCmd(ClientData clientData, Tcl_Interp *interp, int objc,
2939       Tcl_Obj *const *objv)
2940{
2941    Tcl_ObjCmdProc *proc;
2942
2943    proc = Rappture::GetOpFromObj(interp, nLICOps, licOps,
2944                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
2945    if (proc == NULL) {
2946        return TCL_ERROR;
2947    }
2948    return (*proc) (clientData, interp, objc, objv);
2949}
2950
2951static int
2952MoleculeAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
2953              Tcl_Obj *const *objv)
2954{
2955    if (objc == 3) {
2956        const char *name = Tcl_GetString(objv[2]);
2957        if (!g_renderer->addMolecule(name)) {
2958            Tcl_AppendResult(interp, "Failed to create molecule", (char*)NULL);
2959            return TCL_ERROR;
2960        }
2961    } else {
2962        if (!g_renderer->addMolecule("all")) {
2963            Tcl_AppendResult(interp, "Failed to create molecule for one or more data sets", (char*)NULL);
2964            return TCL_ERROR;
2965        }
2966    }
2967    return TCL_OK;
2968}
2969
2970static int
2971MoleculeAtomVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
2972                         Tcl_Obj *const *objv)
2973{
2974    bool state;
2975    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
2976        return TCL_ERROR;
2977    }
2978    if (objc == 4) {
2979        const char *name = Tcl_GetString(objv[3]);
2980        g_renderer->setMoleculeAtomVisibility(name, state);
2981    } else {
2982        g_renderer->setMoleculeAtomVisibility("all", state);
2983    }
2984    return TCL_OK;
2985}
2986
2987static int
2988MoleculeAtomScalingOp(ClientData clientData, Tcl_Interp *interp, int objc,
2989                      Tcl_Obj *const *objv)
2990{
2991    Molecule::AtomScaling scaling;
2992    const char *scalingOpt = Tcl_GetString(objv[2]);
2993    if (scalingOpt[0] == 'v' && strcmp(scalingOpt, "van_der_waals") == 0) {
2994        scaling = Molecule::VAN_DER_WAALS_RADIUS;
2995    } else if (scalingOpt[0] == 'c' && strcmp(scalingOpt, "covalent") == 0) {
2996        scaling = Molecule::COVALENT_RADIUS;
2997    } else if (scalingOpt[0] == 'a' && strcmp(scalingOpt, "atomic") == 0) {
2998        scaling = Molecule::ATOMIC_RADIUS;
2999    } else if (scalingOpt[0] == 'n' && strcmp(scalingOpt, "none") == 0) {
3000        scaling = Molecule::NO_ATOM_SCALING;
3001    } else {
3002        Tcl_AppendResult(interp, "bad atomscale option \"", scalingOpt,
3003                         "\": should be van_der_waals, covalent, atomic, or none", (char*)NULL);
3004        return TCL_ERROR;
3005    }
3006    if (objc == 4) {
3007        const char *name = Tcl_GetString(objv[3]);
3008        g_renderer->setMoleculeAtomScaling(name, scaling);
3009    } else {
3010        g_renderer->setMoleculeAtomScaling("all", scaling);
3011    }
3012    return TCL_OK;
3013}
3014
3015static int
3016MoleculeBondVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
3017                         Tcl_Obj *const *objv)
3018{
3019    bool state;
3020    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3021        return TCL_ERROR;
3022    }
3023    if (objc == 4) {
3024        const char *name = Tcl_GetString(objv[3]);
3025        g_renderer->setMoleculeBondVisibility(name, state);
3026    } else {
3027        g_renderer->setMoleculeBondVisibility("all", state);
3028    }
3029    return TCL_OK;
3030}
3031
3032static int
3033MoleculeColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
3034                   Tcl_Obj *const *objv)
3035{
3036    const char *colorMapName = Tcl_GetString(objv[2]);
3037    if (objc == 4) {
3038        const char *dataSetName = Tcl_GetString(objv[3]);
3039        g_renderer->setMoleculeColorMap(dataSetName, colorMapName);
3040    } else {
3041        g_renderer->setMoleculeColorMap("all", colorMapName);
3042    }
3043    return TCL_OK;
3044}
3045
3046static int
3047MoleculeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
3048                 Tcl_Obj *const *objv)
3049{
3050    if (objc == 3) {
3051        const char *name = Tcl_GetString(objv[2]);
3052        g_renderer->deleteMolecule(name);
3053    } else {
3054        g_renderer->deleteMolecule("all");
3055    }
3056    return TCL_OK;
3057}
3058
3059static int
3060MoleculeEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
3061                         Tcl_Obj *const *objv)
3062{
3063    bool state;
3064    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3065        return TCL_ERROR;
3066    }
3067    if (objc == 4) {
3068        const char *name = Tcl_GetString(objv[3]);
3069        g_renderer->setMoleculeEdgeVisibility(name, state);
3070    } else {
3071        g_renderer->setMoleculeEdgeVisibility("all", state);
3072    }
3073    return TCL_OK;
3074}
3075
3076static int
3077MoleculeLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
3078                   Tcl_Obj *const *objv)
3079{
3080    bool state;
3081    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3082        return TCL_ERROR;
3083    }
3084    if (objc == 4) {
3085        const char *name = Tcl_GetString(objv[3]);
3086        g_renderer->setMoleculeLighting(name, state);
3087    } else {
3088        g_renderer->setMoleculeLighting("all", state);
3089    }
3090    return TCL_OK;
3091}
3092
3093static int
3094MoleculeLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
3095                    Tcl_Obj *const *objv)
3096{
3097    float color[3];
3098    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
3099        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
3100        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
3101        return TCL_ERROR;
3102    }
3103    if (objc == 6) {
3104        const char *name = Tcl_GetString(objv[5]);
3105        g_renderer->setMoleculeEdgeColor(name, color);
3106    } else {
3107        g_renderer->setMoleculeEdgeColor("all", color);
3108    }
3109    return TCL_OK;
3110}
3111
3112static int
3113MoleculeLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
3114                    Tcl_Obj *const *objv)
3115{
3116    float width;
3117    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
3118        return TCL_ERROR;
3119    }
3120    if (objc == 4) {
3121        const char *name = Tcl_GetString(objv[3]);
3122        g_renderer->setMoleculeEdgeWidth(name, width);
3123    } else {
3124        g_renderer->setMoleculeEdgeWidth("all", width);
3125    }
3126    return TCL_OK;
3127}
3128
3129static int
3130MoleculeOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
3131                  Tcl_Obj *const *objv)
3132{
3133    double opacity;
3134    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
3135        return TCL_ERROR;
3136    }
3137    if (objc == 4) {
3138        const char *name = Tcl_GetString(objv[3]);
3139        g_renderer->setMoleculeOpacity(name, opacity);
3140    } else {
3141        g_renderer->setMoleculeOpacity("all", opacity);
3142    }
3143    return TCL_OK;
3144}
3145
3146static int
3147MoleculeOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
3148                 Tcl_Obj *const *objv)
3149{
3150    double quat[4];
3151    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
3152        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
3153        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
3154        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
3155        return TCL_ERROR;
3156    }
3157    if (objc == 7) {
3158        const char *name = Tcl_GetString(objv[6]);
3159        g_renderer->setMoleculeOrientation(name, quat);
3160    } else {
3161        g_renderer->setMoleculeOrientation("all", quat);
3162    }
3163    return TCL_OK;
3164}
3165
3166static int
3167MoleculePositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
3168                   Tcl_Obj *const *objv)
3169{
3170    double pos[3];
3171    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
3172        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
3173        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
3174        return TCL_ERROR;
3175    }
3176    if (objc == 6) {
3177        const char *name = Tcl_GetString(objv[5]);
3178        g_renderer->setMoleculePosition(name, pos);
3179    } else {
3180        g_renderer->setMoleculePosition("all", pos);
3181    }
3182    return TCL_OK;
3183}
3184
3185static int
3186MoleculeScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
3187                Tcl_Obj *const *objv)
3188{
3189    double scale[3];
3190    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
3191        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
3192        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
3193        return TCL_ERROR;
3194    }
3195    if (objc == 6) {
3196        const char *name = Tcl_GetString(objv[5]);
3197        g_renderer->setMoleculeScale(name, scale);
3198    } else {
3199        g_renderer->setMoleculeScale("all", scale);
3200    }
3201    return TCL_OK;
3202}
3203
3204static int
3205MoleculeVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
3206                  Tcl_Obj *const *objv)
3207{
3208    bool state;
3209    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3210        return TCL_ERROR;
3211    }
3212    if (objc == 4) {
3213        const char *name = Tcl_GetString(objv[3]);
3214        g_renderer->setMoleculeVisibility(name, state);
3215    } else {
3216        g_renderer->setMoleculeVisibility("all", state);
3217    }
3218    return TCL_OK;
3219}
3220
3221static int
3222MoleculeWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc,
3223                    Tcl_Obj *const *objv)
3224{
3225    bool state;
3226    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3227        return TCL_ERROR;
3228    }
3229    if (objc == 4) {
3230        const char *name = Tcl_GetString(objv[3]);
3231        g_renderer->setMoleculeWireframe(name, state);
3232    } else {
3233        g_renderer->setMoleculeWireframe("all", state);
3234    }
3235    return TCL_OK;
3236}
3237
3238static Rappture::CmdSpec moleculeOps[] = {
3239    {"add",        2, MoleculeAddOp, 2, 3, "?dataSetName?"},
3240    {"atoms",      2, MoleculeAtomVisibilityOp, 3, 4, "bool ?dataSetName?"},
3241    {"bonds",      2, MoleculeBondVisibilityOp, 3, 4, "bool ?dataSetName?"},
3242    {"colormap",   1, MoleculeColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
3243    {"delete",     1, MoleculeDeleteOp, 2, 3, "?dataSetName?"},
3244    {"edges",      1, MoleculeEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
3245    {"lighting",   3, MoleculeLightingOp, 3, 4, "bool ?dataSetName?"},
3246    {"linecolor",  5, MoleculeLineColorOp, 5, 6, "r g b ?dataSetName?"},
3247    {"linewidth",  5, MoleculeLineWidthOp, 3, 4, "width ?dataSetName?"},
3248    {"opacity",    2, MoleculeOpacityOp, 3, 4, "value ?dataSetName?"},
3249    {"orient",     2, MoleculeOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
3250    {"pos",        1, MoleculePositionOp, 5, 6, "x y z ?dataSetName?"},
3251    {"rscale",     1, MoleculeAtomScalingOp, 3, 4, "scaling ?dataSetName?"},
3252    {"scale",      1, MoleculeScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
3253    {"visible",    1, MoleculeVisibleOp, 3, 4, "bool ?dataSetName?"},
3254    {"wireframe",  1, MoleculeWireframeOp, 3, 4, "bool ?dataSetName?"}
3255};
3256static int nMoleculeOps = NumCmdSpecs(moleculeOps);
3257
3258static int
3259MoleculeCmd(ClientData clientData, Tcl_Interp *interp, int objc,
3260            Tcl_Obj *const *objv)
3261{
3262    Tcl_ObjCmdProc *proc;
3263
3264    proc = Rappture::GetOpFromObj(interp, nMoleculeOps, moleculeOps,
3265                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
3266    if (proc == NULL) {
3267        return TCL_ERROR;
3268    }
3269    return (*proc) (clientData, interp, objc, objv);
3270}
3271
3272static int
3273PolyDataAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
3274              Tcl_Obj *const *objv)
3275{
3276    if (objc == 3) {
3277        const char *name = Tcl_GetString(objv[2]);
3278        if (!g_renderer->addPolyData(name)) {
3279            Tcl_AppendResult(interp, "Failed to create polydata", (char*)NULL);
3280            return TCL_ERROR;
3281        }
3282    } else {
3283        if (!g_renderer->addPolyData("all")) {
3284            Tcl_AppendResult(interp, "Failed to create polydata for one or more data sets", (char*)NULL);
3285            return TCL_ERROR;
3286        }
3287    }
3288    return TCL_OK;
3289}
3290
3291static int
3292PolyDataDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
3293                 Tcl_Obj *const *objv)
3294{
3295    if (objc == 3) {
3296        const char *name = Tcl_GetString(objv[2]);
3297        g_renderer->deletePolyData(name);
3298    } else {
3299        g_renderer->deletePolyData("all");
3300    }
3301    return TCL_OK;
3302}
3303
3304static int
3305PolyDataColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
3306                Tcl_Obj *const *objv)
3307{
3308    float color[3];
3309    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
3310        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
3311        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
3312        return TCL_ERROR;
3313    }
3314    if (objc == 6) {
3315        const char *name = Tcl_GetString(objv[5]);
3316        g_renderer->setPolyDataColor(name, color);
3317    } else {
3318        g_renderer->setPolyDataColor("all", color);
3319    }
3320    return TCL_OK;
3321}
3322
3323static int
3324PolyDataEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
3325                         Tcl_Obj *const *objv)
3326{
3327    bool state;
3328    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3329        return TCL_ERROR;
3330    }
3331    if (objc == 4) {
3332        const char *name = Tcl_GetString(objv[3]);
3333        g_renderer->setPolyDataEdgeVisibility(name, state);
3334    } else {
3335        g_renderer->setPolyDataEdgeVisibility("all", state);
3336    }
3337    return TCL_OK;
3338}
3339
3340static int
3341PolyDataLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
3342                   Tcl_Obj *const *objv)
3343{
3344    bool state;
3345    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3346        return TCL_ERROR;
3347    }
3348    if (objc == 4) {
3349        const char *name = Tcl_GetString(objv[3]);
3350        g_renderer->setPolyDataLighting(name, state);
3351    } else {
3352        g_renderer->setPolyDataLighting("all", state);
3353    }
3354    return TCL_OK;
3355}
3356
3357static int
3358PolyDataLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
3359                    Tcl_Obj *const *objv)
3360{
3361    float color[3];
3362    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
3363        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
3364        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
3365        return TCL_ERROR;
3366    }
3367    if (objc == 6) {
3368        const char *name = Tcl_GetString(objv[5]);
3369        g_renderer->setPolyDataEdgeColor(name, color);
3370    } else {
3371        g_renderer->setPolyDataEdgeColor("all", color);
3372    }
3373    return TCL_OK;
3374}
3375
3376static int
3377PolyDataLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
3378                    Tcl_Obj *const *objv)
3379{
3380    float width;
3381    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
3382        return TCL_ERROR;
3383    }
3384    if (objc == 4) {
3385        const char *name = Tcl_GetString(objv[3]);
3386        g_renderer->setPolyDataEdgeWidth(name, width);
3387    } else {
3388        g_renderer->setPolyDataEdgeWidth("all", width);
3389    }
3390    return TCL_OK;
3391}
3392
3393static int
3394PolyDataOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
3395                  Tcl_Obj *const *objv)
3396{
3397    double opacity;
3398    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
3399        return TCL_ERROR;
3400    }
3401    if (objc == 4) {
3402        const char *name = Tcl_GetString(objv[3]);
3403        g_renderer->setPolyDataOpacity(name, opacity);
3404    } else {
3405        g_renderer->setPolyDataOpacity("all", opacity);
3406    }
3407    return TCL_OK;
3408}
3409
3410static int
3411PolyDataOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
3412                 Tcl_Obj *const *objv)
3413{
3414    double quat[4];
3415    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
3416        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
3417        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
3418        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
3419        return TCL_ERROR;
3420    }
3421    if (objc == 7) {
3422        const char *name = Tcl_GetString(objv[6]);
3423        g_renderer->setPolyDataOrientation(name, quat);
3424    } else {
3425        g_renderer->setPolyDataOrientation("all", quat);
3426    }
3427    return TCL_OK;
3428}
3429
3430static int
3431PolyDataPointSizeOp(ClientData clientData, Tcl_Interp *interp, int objc,
3432                    Tcl_Obj *const *objv)
3433{
3434    float size;
3435    if (GetFloatFromObj(interp, objv[2], &size) != TCL_OK) {
3436        return TCL_ERROR;
3437    }
3438    if (objc == 4) {
3439        const char *name = Tcl_GetString(objv[3]);
3440        g_renderer->setPolyDataPointSize(name, size);
3441    } else {
3442        g_renderer->setPolyDataPointSize("all", size);
3443    }
3444    return TCL_OK;
3445}
3446
3447static int
3448PolyDataPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
3449                   Tcl_Obj *const *objv)
3450{
3451    double pos[3];
3452    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
3453        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
3454        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
3455        return TCL_ERROR;
3456    }
3457    if (objc == 6) {
3458        const char *name = Tcl_GetString(objv[5]);
3459        g_renderer->setPolyDataPosition(name, pos);
3460    } else {
3461        g_renderer->setPolyDataPosition("all", pos);
3462    }
3463    return TCL_OK;
3464}
3465
3466static int
3467PolyDataScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
3468                Tcl_Obj *const *objv)
3469{
3470    double scale[3];
3471    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
3472        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
3473        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
3474        return TCL_ERROR;
3475    }
3476    if (objc == 6) {
3477        const char *name = Tcl_GetString(objv[5]);
3478        g_renderer->setPolyDataScale(name, scale);
3479    } else {
3480        g_renderer->setPolyDataScale("all", scale);
3481    }
3482    return TCL_OK;
3483}
3484
3485static int
3486PolyDataVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
3487                  Tcl_Obj *const *objv)
3488{
3489    bool state;
3490    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3491        return TCL_ERROR;
3492    }
3493    if (objc == 4) {
3494        const char *name = Tcl_GetString(objv[3]);
3495        g_renderer->setPolyDataVisibility(name, state);
3496    } else {
3497        g_renderer->setPolyDataVisibility("all", state);
3498    }
3499    return TCL_OK;
3500}
3501
3502static int
3503PolyDataWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc,
3504                    Tcl_Obj *const *objv)
3505{
3506    bool state;
3507    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3508        return TCL_ERROR;
3509    }
3510    if (objc == 4) {
3511        const char *name = Tcl_GetString(objv[3]);
3512        g_renderer->setPolyDataWireframe(name, state);
3513    } else {
3514        g_renderer->setPolyDataWireframe("all", state);
3515    }
3516    return TCL_OK;
3517}
3518
3519static Rappture::CmdSpec polyDataOps[] = {
3520    {"add",       1, PolyDataAddOp, 2, 3, "?dataSetName?"},
3521    {"color",     1, PolyDataColorOp, 5, 6, "r g b ?dataSetName?"},
3522    {"delete",    1, PolyDataDeleteOp, 2, 3, "?dataSetName?"},
3523    {"edges",     1, PolyDataEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
3524    {"lighting",  3, PolyDataLightingOp, 3, 4, "bool ?dataSetName?"},
3525    {"linecolor", 5, PolyDataLineColorOp, 5, 6, "r g b ?dataSetName?"},
3526    {"linewidth", 5, PolyDataLineWidthOp, 3, 4, "width ?dataSetName?"},
3527    {"opacity",   2, PolyDataOpacityOp, 3, 4, "value ?dataSetName?"},
3528    {"orient",    2, PolyDataOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
3529    {"pos",       2, PolyDataPositionOp, 5, 6, "x y z ?dataSetName?"},
3530    {"ptsize",    2, PolyDataPointSizeOp, 3, 4, "size ?dataSetName?"},
3531    {"scale",     1, PolyDataScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
3532    {"visible",   1, PolyDataVisibleOp, 3, 4, "bool ?dataSetName?"},
3533    {"wireframe", 1, PolyDataWireframeOp, 3, 4, "bool ?dataSetName?"}
3534};
3535static int nPolyDataOps = NumCmdSpecs(polyDataOps);
3536
3537static int
3538PolyDataCmd(ClientData clientData, Tcl_Interp *interp, int objc,
3539            Tcl_Obj *const *objv)
3540{
3541    Tcl_ObjCmdProc *proc;
3542
3543    proc = Rappture::GetOpFromObj(interp, nPolyDataOps, polyDataOps,
3544                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
3545    if (proc == NULL) {
3546        return TCL_ERROR;
3547    }
3548    return (*proc) (clientData, interp, objc, objv);
3549}
3550
3551static int
3552PseudoColorAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
3553               Tcl_Obj *const *objv)
3554{
3555    if (objc == 3) {
3556        const char *name = Tcl_GetString(objv[2]);
3557        if (!g_renderer->addPseudoColor(name)) {
3558            Tcl_AppendResult(interp, "Failed to create pseudocolor", (char*)NULL);
3559            return TCL_ERROR;
3560        }
3561    } else {
3562        if (!g_renderer->addPseudoColor("all")) {
3563            Tcl_AppendResult(interp, "Failed to create pseudocolor for one or more data sets", (char*)NULL);
3564            return TCL_ERROR;
3565        }
3566    }
3567    return TCL_OK;
3568}
3569
3570static int
3571PseudoColorColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
3572                   Tcl_Obj *const *objv)
3573{
3574    float color[3];
3575    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
3576        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
3577        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
3578        return TCL_ERROR;
3579    }
3580    if (objc == 6) {
3581        const char *name = Tcl_GetString(objv[5]);
3582        g_renderer->setPseudoColorColor(name, color);
3583    } else {
3584        g_renderer->setPseudoColorColor("all", color);
3585    }
3586    return TCL_OK;
3587}
3588
3589static int
3590PseudoColorColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
3591                      Tcl_Obj *const *objv)
3592{
3593    const char *colorMapName = Tcl_GetString(objv[2]);
3594    if (objc == 4) {
3595        const char *dataSetName = Tcl_GetString(objv[3]);
3596        g_renderer->setPseudoColorColorMap(dataSetName, colorMapName);
3597    } else {
3598        g_renderer->setPseudoColorColorMap("all", colorMapName);
3599    }
3600    return TCL_OK;
3601}
3602
3603static int
3604PseudoColorColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc,
3605                       Tcl_Obj *const *objv)
3606{
3607    PseudoColor::ColorMode mode;
3608    const char *str = Tcl_GetString(objv[2]);
3609    if (str[0] == 'c' && strcmp(str, "ccolor") == 0) {
3610        mode = PseudoColor::COLOR_CONSTANT;
3611    } else if (str[0] == 's' && strcmp(str, "scalar") == 0) {
3612        mode = PseudoColor::COLOR_BY_SCALAR;
3613    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
3614        mode = PseudoColor::COLOR_BY_VECTOR_MAGNITUDE;
3615    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
3616        mode = PseudoColor::COLOR_BY_VECTOR_X;
3617    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
3618        mode = PseudoColor::COLOR_BY_VECTOR_Y;
3619    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
3620        mode = PseudoColor::COLOR_BY_VECTOR_Z;
3621    } else {
3622        Tcl_AppendResult(interp, "bad color mode option \"", str,
3623                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor'", (char*)NULL);
3624        return TCL_ERROR;
3625    }
3626    if (objc == 4) {
3627        const char *name = Tcl_GetString(objv[3]);
3628        g_renderer->setPseudoColorColorMode(name, mode);
3629    } else {
3630        g_renderer->setPseudoColorColorMode("all", mode);
3631    }
3632    return TCL_OK;
3633}
3634
3635static int
3636PseudoColorDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
3637                  Tcl_Obj *const *objv)
3638{
3639    if (objc == 3) {
3640        const char *name = Tcl_GetString(objv[2]);
3641        g_renderer->deletePseudoColor(name);
3642    } else {
3643        g_renderer->deletePseudoColor("all");
3644    }
3645    return TCL_OK;
3646}
3647
3648static int
3649PseudoColorEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
3650                            Tcl_Obj *const *objv)
3651{
3652    bool state;
3653    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3654        return TCL_ERROR;
3655    }
3656    if (objc == 4) {
3657        const char *name = Tcl_GetString(objv[3]);
3658        g_renderer->setPseudoColorEdgeVisibility(name, state);
3659    } else {
3660        g_renderer->setPseudoColorEdgeVisibility("all", state);
3661    }
3662    return TCL_OK;
3663}
3664
3665static int
3666PseudoColorLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
3667                      Tcl_Obj *const *objv)
3668{
3669    bool state;
3670    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3671        return TCL_ERROR;
3672    }
3673    if (objc == 4) {
3674        const char *name = Tcl_GetString(objv[3]);
3675        g_renderer->setPseudoColorLighting(name, state);
3676    } else {
3677        g_renderer->setPseudoColorLighting("all", state);
3678    }
3679    return TCL_OK;
3680}
3681
3682static int
3683PseudoColorLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
3684                       Tcl_Obj *const *objv)
3685{
3686    float color[3];
3687    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
3688        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
3689        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
3690        return TCL_ERROR;
3691    }
3692    if (objc == 6) {
3693        const char *name = Tcl_GetString(objv[5]);
3694        g_renderer->setPseudoColorEdgeColor(name, color);
3695    } else {
3696        g_renderer->setPseudoColorEdgeColor("all", color);
3697    }
3698    return TCL_OK;
3699}
3700
3701static int
3702PseudoColorLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
3703                       Tcl_Obj *const *objv)
3704{
3705    float width;
3706    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
3707        return TCL_ERROR;
3708    }
3709    if (objc == 4) {
3710        const char *name = Tcl_GetString(objv[3]);
3711        g_renderer->setPseudoColorEdgeWidth(name, width);
3712    } else {
3713        g_renderer->setPseudoColorEdgeWidth("all", width);
3714    }
3715    return TCL_OK;
3716}
3717
3718static int
3719PseudoColorOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
3720                     Tcl_Obj *const *objv)
3721{
3722    double opacity;
3723    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
3724        return TCL_ERROR;
3725    }
3726    if (objc == 4) {
3727        const char *name = Tcl_GetString(objv[3]);
3728        g_renderer->setPseudoColorOpacity(name, opacity);
3729    } else {
3730        g_renderer->setPseudoColorOpacity("all", opacity);
3731    }
3732    return TCL_OK;
3733}
3734
3735static int
3736PseudoColorOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
3737                    Tcl_Obj *const *objv)
3738{
3739    double quat[4];
3740    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
3741        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
3742        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
3743        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
3744        return TCL_ERROR;
3745    }
3746    if (objc == 7) {
3747        const char *name = Tcl_GetString(objv[6]);
3748        g_renderer->setPseudoColorOrientation(name, quat);
3749    } else {
3750        g_renderer->setPseudoColorOrientation("all", quat);
3751    }
3752    return TCL_OK;
3753}
3754
3755static int
3756PseudoColorPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
3757                      Tcl_Obj *const *objv)
3758{
3759    double pos[3];
3760    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
3761        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
3762        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
3763        return TCL_ERROR;
3764    }
3765    if (objc == 6) {
3766        const char *name = Tcl_GetString(objv[5]);
3767        g_renderer->setPseudoColorPosition(name, pos);
3768    } else {
3769        g_renderer->setPseudoColorPosition("all", pos);
3770    }
3771    return TCL_OK;
3772}
3773
3774static int
3775PseudoColorScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
3776                   Tcl_Obj *const *objv)
3777{
3778    double scale[3];
3779    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
3780        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
3781        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
3782        return TCL_ERROR;
3783    }
3784    if (objc == 6) {
3785        const char *name = Tcl_GetString(objv[5]);
3786        g_renderer->setPseudoColorScale(name, scale);
3787    } else {
3788        g_renderer->setPseudoColorScale("all", scale);
3789    }
3790    return TCL_OK;
3791}
3792
3793static int
3794PseudoColorVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
3795                     Tcl_Obj *const *objv)
3796{
3797    bool state;
3798    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3799        return TCL_ERROR;
3800    }
3801    if (objc == 4) {
3802        const char *name = Tcl_GetString(objv[3]);
3803        g_renderer->setPseudoColorVisibility(name, state);
3804    } else {
3805        g_renderer->setPseudoColorVisibility("all", state);
3806    }
3807    return TCL_OK;
3808}
3809
3810static int
3811PseudoColorWireframeOp(ClientData clientData, Tcl_Interp *interp, int objc,
3812                       Tcl_Obj *const *objv)
3813{
3814    bool state;
3815    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3816        return TCL_ERROR;
3817    }
3818    if (objc == 4) {
3819        const char *name = Tcl_GetString(objv[3]);
3820        g_renderer->setPseudoColorWireframe(name, state);
3821    } else {
3822        g_renderer->setPseudoColorWireframe("all", state);
3823    }
3824    return TCL_OK;
3825}
3826
3827static Rappture::CmdSpec pseudoColorOps[] = {
3828    {"add",       1, PseudoColorAddOp, 2, 3, "?dataSetName?"},
3829    {"ccolor",    2, PseudoColorColorOp, 5, 6, "r g b ?dataSetName?"},
3830    {"colormap",  7, PseudoColorColorMapOp, 3, 4, "colorMapName ?dataSetNme?"},
3831    {"colormode", 7, PseudoColorColorModeOp, 3, 4, "mode ?dataSetNme?"},
3832    {"delete",    1, PseudoColorDeleteOp, 2, 3, "?dataSetName?"},
3833    {"edges",     1, PseudoColorEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
3834    {"lighting",  3, PseudoColorLightingOp, 3, 4, "bool ?dataSetName?"},
3835    {"linecolor", 5, PseudoColorLineColorOp, 5, 6, "r g b ?dataSetName?"},
3836    {"linewidth", 5, PseudoColorLineWidthOp, 3, 4, "width ?dataSetName?"},
3837    {"opacity",   2, PseudoColorOpacityOp, 3, 4, "value ?dataSetName?"},
3838    {"orient",    2, PseudoColorOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
3839    {"pos",       1, PseudoColorPositionOp, 5, 6, "x y z ?dataSetName?"},
3840    {"scale",     1, PseudoColorScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
3841    {"visible",   1, PseudoColorVisibleOp, 3, 4, "bool ?dataSetName?"},
3842    {"wireframe", 1, PseudoColorWireframeOp, 3, 4, "bool ?dataSetName?"}
3843};
3844static int nPseudoColorOps = NumCmdSpecs(pseudoColorOps);
3845
3846static int
3847PseudoColorCmd(ClientData clientData, Tcl_Interp *interp, int objc,
3848               Tcl_Obj *const *objv)
3849{
3850    Tcl_ObjCmdProc *proc;
3851
3852    proc = Rappture::GetOpFromObj(interp, nPseudoColorOps, pseudoColorOps,
3853                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
3854    if (proc == NULL) {
3855        return TCL_ERROR;
3856    }
3857    return (*proc) (clientData, interp, objc, objv);
3858}
3859
3860static int
3861RendererClipPlaneOp(ClientData clientData, Tcl_Interp *interp, int objc,
3862                    Tcl_Obj *const *objv)
3863{
3864    const char *string = Tcl_GetString(objv[2]);
3865    char c = string[0];
3866    Renderer::Axis axis;
3867    if ((c == 'x') && (strcmp(string, "x") == 0)) {
3868        axis = Renderer::X_AXIS;
3869    } else if ((c == 'y') && (strcmp(string, "y") == 0)) {
3870        axis = Renderer::Y_AXIS;
3871    } else if ((c == 'z') && (strcmp(string, "z") == 0)) {
3872        axis = Renderer::Z_AXIS;
3873    } else {
3874        Tcl_AppendResult(interp, "bad clipplane option \"", string,
3875                         "\": should be axisName ratio direction", (char*)NULL);
3876        return TCL_ERROR;
3877    }
3878    double ratio;
3879    if (Tcl_GetDoubleFromObj(interp, objv[3], &ratio) != TCL_OK) {
3880        return TCL_ERROR;
3881    }
3882    int direction;
3883    if (Tcl_GetIntFromObj(interp, objv[4], &direction) != TCL_OK) {
3884        return TCL_ERROR;
3885    }
3886    g_renderer->setClipPlane(axis, ratio, direction);
3887    return TCL_OK;
3888}
3889
3890static int
3891RendererDepthPeelingOp(ClientData clientData, Tcl_Interp *interp, int objc,
3892                       Tcl_Obj *const *objv)
3893{
3894    bool state;
3895    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3896        return TCL_ERROR;
3897    }
3898    g_renderer->setUseDepthPeeling(state);
3899    return TCL_OK;
3900}
3901
3902static int
3903RendererTwoSidedLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
3904                           Tcl_Obj *const *objv)
3905{
3906    bool state;
3907    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
3908        return TCL_ERROR;
3909    }
3910    g_renderer->setUseTwoSidedLighting(state);
3911    return TCL_OK;
3912}
3913
3914static int
3915RendererRenderOp(ClientData clientData, Tcl_Interp *interp, int objc,
3916                 Tcl_Obj *const *objv)
3917{
3918    g_renderer->eventuallyRender();
3919    return TCL_OK;
3920}
3921
3922static Rappture::CmdSpec rendererOps[] = {
3923    {"clipplane",  1, RendererClipPlaneOp, 5, 5, "axis ratio direction"},
3924    {"depthpeel",  1, RendererDepthPeelingOp, 3, 3, "bool"},
3925    {"light2side", 1, RendererTwoSidedLightingOp, 3, 3, "bool"},
3926    {"render",     1, RendererRenderOp, 2, 2, ""}
3927};
3928static int nRendererOps = NumCmdSpecs(rendererOps);
3929
3930static int
3931RendererCmd(ClientData clientData, Tcl_Interp *interp, int objc,
3932            Tcl_Obj *const *objv)
3933{
3934    Tcl_ObjCmdProc *proc;
3935
3936    proc = Rappture::GetOpFromObj(interp, nRendererOps, rendererOps,
3937                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
3938    if (proc == NULL) {
3939        return TCL_ERROR;
3940    }
3941    return (*proc) (clientData, interp, objc, objv);
3942}
3943
3944static int
3945ScreenBgColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
3946                Tcl_Obj *const *objv)
3947{
3948    float color[3];
3949
3950    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
3951        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
3952        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
3953        return TCL_ERROR;
3954    }
3955
3956    g_renderer->setBackgroundColor(color);
3957    return TCL_OK;
3958}
3959
3960static int
3961ScreenSizeOp(ClientData clientData, Tcl_Interp *interp, int objc,
3962             Tcl_Obj *const *objv)
3963{
3964    int width, height;
3965
3966    if (Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK ||
3967        Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) {
3968        return TCL_ERROR;
3969    }
3970
3971    g_renderer->setWindowSize(width, height);
3972    return TCL_OK;
3973}
3974
3975static Rappture::CmdSpec screenOps[] = {
3976    {"bgcolor", 1, ScreenBgColorOp, 5, 5, "r g b"},
3977    {"size", 1, ScreenSizeOp, 4, 4, "width height"}
3978};
3979static int nScreenOps = NumCmdSpecs(screenOps);
3980
3981static int
3982ScreenCmd(ClientData clientData, Tcl_Interp *interp, int objc,
3983          Tcl_Obj *const *objv)
3984{
3985    Tcl_ObjCmdProc *proc;
3986
3987    proc = Rappture::GetOpFromObj(interp, nScreenOps, screenOps,
3988                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
3989    if (proc == NULL) {
3990        return TCL_ERROR;
3991    }
3992    return (*proc) (clientData, interp, objc, objv);
3993}
3994
3995static int
3996StreamlinesAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
3997                 Tcl_Obj *const *objv)
3998{
3999    if (objc == 3) {
4000        const char *name = Tcl_GetString(objv[2]);
4001        if (!g_renderer->addStreamlines(name)) {
4002            Tcl_AppendResult(interp, "Failed to create streamlines", (char*)NULL);
4003            return TCL_ERROR;
4004        }
4005    } else {
4006        if (!g_renderer->addStreamlines("all")) {
4007            Tcl_AppendResult(interp, "Failed to create streamlines for one or more data sets", (char*)NULL);
4008            return TCL_ERROR;
4009        }
4010    }
4011    return TCL_OK;
4012}
4013
4014static int
4015StreamlinesColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
4016                   Tcl_Obj *const *objv)
4017{
4018    float color[3];
4019    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
4020        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
4021        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
4022        return TCL_ERROR;
4023    }
4024    if (objc == 6) {
4025        const char *name = Tcl_GetString(objv[5]);
4026        g_renderer->setStreamlinesColor(name, color);
4027    } else {
4028        g_renderer->setStreamlinesColor("all", color);
4029    }
4030    return TCL_OK;
4031}
4032
4033static int
4034StreamlinesColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
4035                      Tcl_Obj *const *objv)
4036{
4037    const char *colorMapName = Tcl_GetString(objv[2]);
4038    if (objc == 4) {
4039        const char *dataSetName = Tcl_GetString(objv[3]);
4040        g_renderer->setStreamlinesColorMap(dataSetName, colorMapName);
4041    } else {
4042        g_renderer->setStreamlinesColorMap("all", colorMapName);
4043    }
4044    return TCL_OK;
4045}
4046
4047static int
4048StreamlinesColorModeOp(ClientData clientData, Tcl_Interp *interp, int objc,
4049                       Tcl_Obj *const *objv)
4050{
4051    Streamlines::ColorMode mode;
4052    const char *str = Tcl_GetString(objv[2]);
4053    if (str[0] == 's' && strcmp(str, "scalar") == 0) {
4054        mode = Streamlines::COLOR_BY_SCALAR;
4055    } else if (str[0] == 'v' && strcmp(str, "vmag") == 0) {
4056        mode = Streamlines::COLOR_BY_VECTOR_MAGNITUDE;
4057    } else if (str[0] == 'v' && strcmp(str, "vx") == 0) {
4058        mode = Streamlines::COLOR_BY_VECTOR_X;
4059    } else if (str[0] == 'v' && strcmp(str, "vy") == 0) {
4060        mode = Streamlines::COLOR_BY_VECTOR_Y;
4061    } else if (str[0] == 'v' && strcmp(str, "vz") == 0) {
4062        mode = Streamlines::COLOR_BY_VECTOR_Z;
4063    } else if (str[0] == 'c' && strcmp(str, "ccolor") == 0) {
4064        mode = Streamlines::COLOR_CONSTANT;
4065    } else {
4066        Tcl_AppendResult(interp, "bad color mode option \"", str,
4067                         "\": should be one of: 'scalar', 'vmag', 'vx', 'vy', 'vz', 'ccolor'", (char*)NULL);
4068        return TCL_ERROR;
4069    }
4070    if (objc == 4) {
4071        const char *dataSetName = Tcl_GetString(objv[3]);
4072        g_renderer->setStreamlinesColorMode(dataSetName, mode);
4073    } else {
4074        g_renderer->setStreamlinesColorMode("all", mode);
4075    }
4076    return TCL_OK;
4077}
4078
4079static int
4080StreamlinesDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
4081                    Tcl_Obj *const *objv)
4082{
4083    if (objc == 3) {
4084        const char *name = Tcl_GetString(objv[2]);
4085        g_renderer->deleteStreamlines(name);
4086    } else {
4087        g_renderer->deleteStreamlines("all");
4088    }
4089    return TCL_OK;
4090}
4091
4092static int
4093StreamlinesEdgeVisibilityOp(ClientData clientData, Tcl_Interp *interp, int objc,
4094                            Tcl_Obj *const *objv)
4095{
4096    bool state;
4097    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
4098        return TCL_ERROR;
4099    }
4100    if (objc == 4) {
4101        const char *name = Tcl_GetString(objv[3]);
4102        g_renderer->setStreamlinesEdgeVisibility(name, state);
4103    } else {
4104        g_renderer->setStreamlinesEdgeVisibility("all", state);
4105    }
4106    return TCL_OK;
4107}
4108
4109static int
4110StreamlinesLengthOp(ClientData clientData, Tcl_Interp *interp, int objc,
4111                    Tcl_Obj *const *objv)
4112{
4113    double length;
4114    if (Tcl_GetDoubleFromObj(interp, objv[2], &length) != TCL_OK) {
4115        return TCL_ERROR;
4116    }
4117    if (objc == 4) {
4118        const char *name = Tcl_GetString(objv[3]);
4119        g_renderer->setStreamlinesLength(name, length);
4120    } else {
4121        g_renderer->setStreamlinesLength("all", length);
4122    }
4123    return TCL_OK;
4124}
4125
4126static int
4127StreamlinesLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
4128                      Tcl_Obj *const *objv)
4129{
4130    bool state;
4131    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
4132        return TCL_ERROR;
4133    }
4134    if (objc == 4) {
4135        const char *name = Tcl_GetString(objv[3]);
4136        g_renderer->setStreamlinesLighting(name, state);
4137    } else {
4138        g_renderer->setStreamlinesLighting("all", state);
4139    }
4140    return TCL_OK;
4141}
4142
4143static int
4144StreamlinesLineColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
4145                       Tcl_Obj *const *objv)
4146{
4147    float color[3];
4148    if (GetFloatFromObj(interp, objv[2], &color[0]) != TCL_OK ||
4149        GetFloatFromObj(interp, objv[3], &color[1]) != TCL_OK ||
4150        GetFloatFromObj(interp, objv[4], &color[2]) != TCL_OK) {
4151        return TCL_ERROR;
4152    }
4153    if (objc == 6) {
4154        const char *name = Tcl_GetString(objv[5]);
4155        g_renderer->setStreamlinesEdgeColor(name, color);
4156    } else {
4157        g_renderer->setStreamlinesEdgeColor("all", color);
4158    }
4159    return TCL_OK;
4160}
4161
4162static int
4163StreamlinesLinesOp(ClientData clientData, Tcl_Interp *interp, int objc,
4164                   Tcl_Obj *const *objv)
4165{
4166    if (objc == 3) {
4167        const char *name = Tcl_GetString(objv[2]);
4168        g_renderer->setStreamlinesTypeToLines(name);
4169    } else {
4170        g_renderer->setStreamlinesTypeToLines("all");
4171    }
4172    return TCL_OK;
4173}
4174
4175static int
4176StreamlinesLineWidthOp(ClientData clientData, Tcl_Interp *interp, int objc,
4177                       Tcl_Obj *const *objv)
4178{
4179    float width;
4180    if (GetFloatFromObj(interp, objv[2], &width) != TCL_OK) {
4181        return TCL_ERROR;
4182    }
4183    if (objc == 4) {
4184        const char *name = Tcl_GetString(objv[3]);
4185        g_renderer->setStreamlinesEdgeWidth(name, width);
4186    } else {
4187        g_renderer->setStreamlinesEdgeWidth("all", width);
4188    }
4189    return TCL_OK;
4190}
4191
4192static int
4193StreamlinesOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
4194                     Tcl_Obj *const *objv)
4195{
4196    double opacity;
4197    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
4198        return TCL_ERROR;
4199    }
4200    if (objc == 4) {
4201        const char *name = Tcl_GetString(objv[3]);
4202        g_renderer->setStreamlinesOpacity(name, opacity);
4203    } else {
4204        g_renderer->setStreamlinesOpacity("all", opacity);
4205    }
4206    return TCL_OK;
4207}
4208
4209static int
4210StreamlinesOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
4211                    Tcl_Obj *const *objv)
4212{
4213    double quat[4];
4214    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
4215        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
4216        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
4217        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
4218        return TCL_ERROR;
4219    }
4220    if (objc == 7) {
4221        const char *name = Tcl_GetString(objv[6]);
4222        g_renderer->setStreamlinesOrientation(name, quat);
4223    } else {
4224        g_renderer->setStreamlinesOrientation("all", quat);
4225    }
4226    return TCL_OK;
4227}
4228
4229static int
4230StreamlinesPositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
4231                      Tcl_Obj *const *objv)
4232{
4233    double pos[3];
4234    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
4235        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
4236        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
4237        return TCL_ERROR;
4238    }
4239    if (objc == 6) {
4240        const char *name = Tcl_GetString(objv[5]);
4241        g_renderer->setStreamlinesPosition(name, pos);
4242    } else {
4243        g_renderer->setStreamlinesPosition("all", pos);
4244    }
4245    return TCL_OK;
4246}
4247
4248static int
4249StreamlinesScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
4250                   Tcl_Obj *const *objv)
4251{
4252    double scale[3];
4253    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
4254        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
4255        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
4256        return TCL_ERROR;
4257    }
4258    if (objc == 6) {
4259        const char *name = Tcl_GetString(objv[5]);
4260        g_renderer->setStreamlinesScale(name, scale);
4261    } else {
4262        g_renderer->setStreamlinesScale("all", scale);
4263    }
4264    return TCL_OK;
4265}
4266
4267static int
4268StreamlinesRibbonsOp(ClientData clientData, Tcl_Interp *interp, int objc,
4269                     Tcl_Obj *const *objv)
4270{
4271    double width, angle;
4272    if (Tcl_GetDoubleFromObj(interp, objv[2], &width) != TCL_OK) {
4273        return TCL_ERROR;
4274    }
4275    if (Tcl_GetDoubleFromObj(interp, objv[3], &angle) != TCL_OK) {
4276        return TCL_ERROR;
4277    }
4278    if (objc == 5) {
4279        const char *name = Tcl_GetString(objv[4]);
4280        g_renderer->setStreamlinesTypeToRibbons(name, width, angle);
4281    } else {
4282        g_renderer->setStreamlinesTypeToRibbons("all", width, angle);
4283    }
4284    return TCL_OK;
4285}
4286
4287static int
4288StreamlinesSeedColorOp(ClientData clientData, Tcl_Interp *interp, int objc,
4289                       Tcl_Obj *const *objv)
4290{
4291    float color[3];
4292    if (GetFloatFromObj(interp, objv[3], &color[0]) != TCL_OK ||
4293        GetFloatFromObj(interp, objv[4], &color[1]) != TCL_OK ||
4294        GetFloatFromObj(interp, objv[5], &color[2]) != TCL_OK) {
4295        return TCL_ERROR;
4296    }
4297    if (objc == 7) {
4298        const char *name = Tcl_GetString(objv[6]);
4299        g_renderer->setStreamlinesSeedColor(name, color);
4300    } else {
4301        g_renderer->setStreamlinesSeedColor("all", color);
4302    }
4303    return TCL_OK;
4304}
4305
4306static int
4307StreamlinesSeedDiskOp(ClientData clientData, Tcl_Interp *interp, int objc,
4308                      Tcl_Obj *const *objv)
4309{
4310    double center[3], normal[3], radius, innerRadius;
4311    int numPoints;
4312    for (int i = 0; i < 3; i++) {
4313        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &center[i]) != TCL_OK) {
4314            return TCL_ERROR;
4315        }
4316        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &normal[i]) != TCL_OK) {
4317            return TCL_ERROR;
4318        }
4319    }
4320    if (Tcl_GetDoubleFromObj(interp, objv[9], &radius) != TCL_OK) {
4321        return TCL_ERROR;
4322    }
4323    if (Tcl_GetDoubleFromObj(interp, objv[10], &innerRadius) != TCL_OK) {
4324        return TCL_ERROR;
4325    }
4326    if (Tcl_GetIntFromObj(interp, objv[11], &numPoints) != TCL_OK) {
4327        return TCL_ERROR;
4328    }
4329    if (objc == 13) {
4330        const char *name = Tcl_GetString(objv[12]);
4331        g_renderer->setStreamlinesSeedToDisk(name, center, normal, radius, innerRadius, numPoints);
4332    } else {
4333        g_renderer->setStreamlinesSeedToDisk("all", center, normal, radius, innerRadius, numPoints);
4334    }
4335    return TCL_OK;
4336}
4337
4338static int
4339StreamlinesSeedFilledMeshOp(ClientData clientData, Tcl_Interp *interp,
4340                            int objc, Tcl_Obj *const *objv)
4341{
4342    int numPoints = 0;
4343    if (Tcl_GetIntFromObj(interp, objv[3], &numPoints) != TCL_OK ||
4344        numPoints < 1) {
4345        return TCL_ERROR;
4346    }
4347    const char *string = Tcl_GetString(objv[4]);
4348    char c = string[0];
4349    if ((c != 'd') || (strcmp(string, "data") != 0)) {
4350        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
4351                         string, "\": should be data", (char*)NULL);
4352        return TCL_ERROR;
4353    }
4354    string = Tcl_GetString(objv[5]);
4355    c = string[0];
4356    if ((c != 'f') || (strcmp(string, "follows") != 0)) {
4357        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
4358                         string, "\": should be follows", (char*)NULL);
4359        return TCL_ERROR;
4360    }
4361    int nbytes = 0;
4362    if (Tcl_GetIntFromObj(interp, objv[6], &nbytes) != TCL_OK ||
4363        nbytes < 0) {
4364        return TCL_ERROR;
4365    }
4366    char *data = (char *)malloc(nbytes);
4367    size_t bytesRead = SocketRead(data, nbytes);
4368    if (bytesRead < 0) {
4369        free(data);
4370        return TCL_ERROR;
4371    }
4372    if (objc == 8) {
4373        const char *name = Tcl_GetString(objv[7]);
4374        if (!g_renderer->setStreamlinesSeedToFilledMesh(name, data, nbytes,
4375                                                        numPoints)) {
4376            free(data);
4377            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
4378            return TCL_ERROR;
4379        }
4380    } else {
4381        if (!g_renderer->setStreamlinesSeedToFilledMesh("all", data, nbytes,
4382                                                        numPoints)) {
4383            free(data);
4384            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
4385            return TCL_ERROR;
4386        }
4387    }
4388    free(data);
4389    return TCL_OK;
4390}
4391
4392static int
4393StreamlinesSeedMeshPointsOp(ClientData clientData, Tcl_Interp *interp,
4394                            int objc, Tcl_Obj *const *objv)
4395{
4396    const char *string = Tcl_GetString(objv[3]);
4397    char c = string[0];
4398    if ((c != 'd') || (strcmp(string, "data") != 0)) {
4399        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
4400                         string, "\": should be data", (char*)NULL);
4401        return TCL_ERROR;
4402    }
4403    string = Tcl_GetString(objv[4]);
4404    c = string[0];
4405    if ((c != 'f') || (strcmp(string, "follows") != 0)) {
4406        Tcl_AppendResult(interp, "bad streamlines seed fmesh option \"",
4407                         string, "\": should be follows", (char*)NULL);
4408        return TCL_ERROR;
4409    }
4410    int nbytes = 0;
4411    if (Tcl_GetIntFromObj(interp, objv[5], &nbytes) != TCL_OK ||
4412        nbytes < 0) {
4413        return TCL_ERROR;
4414    }
4415    char *data = (char *)malloc(nbytes);
4416    size_t bytesRead = SocketRead(data, nbytes);
4417    if (bytesRead < 0) {
4418        free(data);
4419        return TCL_ERROR;
4420    }
4421    if (objc == 7) {
4422        const char *name = Tcl_GetString(objv[6]);
4423        if (!g_renderer->setStreamlinesSeedToMeshPoints(name, data, nbytes)) {
4424            free(data);
4425            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
4426            return TCL_ERROR;
4427        }
4428    } else {
4429        if (!g_renderer->setStreamlinesSeedToMeshPoints("all", data, nbytes)) {
4430            free(data);
4431            Tcl_AppendResult(interp, "Couldn't read mesh data or streamlines not found", (char*)NULL);
4432            return TCL_ERROR;
4433        }
4434    }
4435    free(data);
4436    return TCL_OK;
4437}
4438
4439static int
4440StreamlinesSeedPointsOp(ClientData clientData, Tcl_Interp *interp, int objc,
4441                        Tcl_Obj *const *objv)
4442{
4443    if (objc == 4) {
4444        const char *name = Tcl_GetString(objv[3]);
4445        g_renderer->setStreamlinesSeedToMeshPoints(name);
4446    } else {
4447        g_renderer->setStreamlinesSeedToMeshPoints("all");
4448    }
4449    return TCL_OK;
4450}
4451
4452static int
4453StreamlinesSeedPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
4454                         Tcl_Obj *const *objv)
4455{
4456    double center[3], normal[3], angle, radius;
4457    int numSides;
4458    for (int i = 0; i < 3; i++) {
4459        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &center[i]) != TCL_OK) {
4460            return TCL_ERROR;
4461        }
4462        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &normal[i]) != TCL_OK) {
4463            return TCL_ERROR;
4464        }
4465    }
4466    if (Tcl_GetDoubleFromObj(interp, objv[9], &angle) != TCL_OK) {
4467        return TCL_ERROR;
4468    }
4469    if (Tcl_GetDoubleFromObj(interp, objv[10], &radius) != TCL_OK) {
4470        return TCL_ERROR;
4471    }
4472    if (Tcl_GetIntFromObj(interp, objv[11], &numSides) != TCL_OK) {
4473        return TCL_ERROR;
4474    }
4475    if (numSides < 3) {
4476        Tcl_AppendResult(interp, "numSides must be 3 or greater", (char*)NULL);
4477        return TCL_ERROR;
4478    }
4479    if (objc == 13) {
4480        const char *name = Tcl_GetString(objv[12]);
4481        g_renderer->setStreamlinesSeedToPolygon(name, center, normal, angle, radius, numSides);
4482    } else {
4483        g_renderer->setStreamlinesSeedToPolygon("all", center, normal, angle, radius, numSides);
4484    }
4485    return TCL_OK;
4486}
4487
4488static int
4489StreamlinesSeedFilledPolygonOp(ClientData clientData, Tcl_Interp *interp, int objc,
4490                               Tcl_Obj *const *objv)
4491{
4492    double center[3], normal[3], angle, radius;
4493    int numSides, numPoints;
4494    for (int i = 0; i < 3; i++) {
4495        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &center[i]) != TCL_OK) {
4496            return TCL_ERROR;
4497        }
4498        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &normal[i]) != TCL_OK) {
4499            return TCL_ERROR;
4500        }
4501    }
4502    if (Tcl_GetDoubleFromObj(interp, objv[9], &angle) != TCL_OK) {
4503        return TCL_ERROR;
4504    }
4505    if (Tcl_GetDoubleFromObj(interp, objv[10], &radius) != TCL_OK) {
4506        return TCL_ERROR;
4507    }
4508    if (Tcl_GetIntFromObj(interp, objv[11], &numSides) != TCL_OK) {
4509        return TCL_ERROR;
4510    }
4511    if (numSides < 3) {
4512        Tcl_AppendResult(interp, "numSides must be 3 or greater", (char*)NULL);
4513        return TCL_ERROR;
4514    }
4515    if (Tcl_GetIntFromObj(interp, objv[12], &numPoints) != TCL_OK) {
4516        return TCL_ERROR;
4517    }
4518    if (objc == 14) {
4519        const char *name = Tcl_GetString(objv[13]);
4520        g_renderer->setStreamlinesSeedToFilledPolygon(name, center, normal, angle,
4521                                                      radius, numSides, numPoints);
4522    } else {
4523        g_renderer->setStreamlinesSeedToFilledPolygon("all", center, normal, angle,
4524                                                      radius, numSides, numPoints);
4525    }
4526    return TCL_OK;
4527}
4528
4529static int
4530StreamlinesSeedRakeOp(ClientData clientData, Tcl_Interp *interp, int objc,
4531                      Tcl_Obj *const *objv)
4532{
4533    double start[3], end[3];
4534    int numPoints;
4535    for (int i = 0; i < 3; i++) {
4536        if (Tcl_GetDoubleFromObj(interp, objv[3+i], &start[i]) != TCL_OK) {
4537            return TCL_ERROR;
4538        }
4539        if (Tcl_GetDoubleFromObj(interp, objv[6+i], &end[i]) != TCL_OK) {
4540            return TCL_ERROR;
4541        }
4542    }
4543    if (Tcl_GetIntFromObj(interp, objv[9], &numPoints) != TCL_OK) {
4544        return TCL_ERROR;
4545    }
4546    if (objc == 11) {
4547        const char *name = Tcl_GetString(objv[10]);
4548        g_renderer->setStreamlinesSeedToRake(name, start, end, numPoints);
4549    } else {
4550        g_renderer->setStreamlinesSeedToRake("all", start, end, numPoints);
4551    }
4552    return TCL_OK;
4553}
4554
4555static int
4556StreamlinesSeedRandomOp(ClientData clientData, Tcl_Interp *interp, int objc,
4557                        Tcl_Obj *const *objv)
4558{
4559    int numPoints;
4560    if (Tcl_GetIntFromObj(interp, objv[3], &numPoints) != TCL_OK) {
4561        return TCL_ERROR;
4562    }
4563    if (objc == 5) {
4564        const char *name = Tcl_GetString(objv[4]);
4565        g_renderer->setStreamlinesSeedToFilledMesh(name, numPoints);
4566    } else {
4567        g_renderer->setStreamlinesSeedToFilledMesh("all", numPoints);
4568    }
4569    return TCL_OK;
4570}
4571
4572static int
4573StreamlinesSeedVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
4574                         Tcl_Obj *const *objv)
4575{
4576    bool state;
4577    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
4578        return TCL_ERROR;
4579    }
4580    if (objc == 5) {
4581        const char *name = Tcl_GetString(objv[4]);
4582        g_renderer->setStreamlinesSeedVisibility(name, state);
4583    } else {
4584        g_renderer->setStreamlinesSeedVisibility("all", state);
4585    }
4586    return TCL_OK;
4587}
4588
4589static Rappture::CmdSpec streamlinesSeedOps[] = {
4590    {"color",   1, StreamlinesSeedColorOp, 6, 7, "r g b ?dataSetName?"},
4591    {"disk",    1, StreamlinesSeedDiskOp, 12, 13, "centerX centerY centerZ normalX normalY normalZ radius innerRadius numPoints ?dataSetName?"},
4592    {"fmesh",   2, StreamlinesSeedFilledMeshOp, 7, 8, "numPoints data follows nbytes ?dataSetName?"},
4593    {"fpoly",   2, StreamlinesSeedFilledPolygonOp, 13, 14, "centerX centerY centerZ normalX normalY normalZ angle radius numSides numPoints ?dataSetName?"},
4594    {"mesh",    1, StreamlinesSeedMeshPointsOp, 6, 7, "data follows nbytes ?dataSetName?"},
4595    {"points",  3, StreamlinesSeedPointsOp, 3, 4, "?dataSetName?"},
4596    {"polygon", 3, StreamlinesSeedPolygonOp, 12, 13, "centerX centerY centerZ normalX normalY normalZ angle radius numSides ?dataSetName?"},
4597    {"rake",    3, StreamlinesSeedRakeOp, 10, 11, "startX startY startZ endX endY endZ numPoints ?dataSetName?"},
4598    {"random",  3, StreamlinesSeedRandomOp, 4, 5, "numPoints ?dataSetName?"},
4599    {"visible", 1, StreamlinesSeedVisibleOp, 4, 5, "bool ?dataSetName?"}
4600};
4601static int nStreamlinesSeedOps = NumCmdSpecs(streamlinesSeedOps);
4602
4603static int
4604StreamlinesSeedOp(ClientData clientData, Tcl_Interp *interp, int objc,
4605                  Tcl_Obj *const *objv)
4606{
4607    Tcl_ObjCmdProc *proc;
4608
4609    proc = Rappture::GetOpFromObj(interp, nStreamlinesSeedOps, streamlinesSeedOps,
4610                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
4611    if (proc == NULL) {
4612        return TCL_ERROR;
4613    }
4614    return (*proc) (clientData, interp, objc, objv);
4615}
4616
4617static int
4618StreamlinesTubesOp(ClientData clientData, Tcl_Interp *interp, int objc,
4619                   Tcl_Obj *const *objv)
4620{
4621    int numSides;
4622    double radius;
4623    if (Tcl_GetIntFromObj(interp, objv[2], &numSides) != TCL_OK) {
4624        return TCL_ERROR;
4625    }
4626    if (Tcl_GetDoubleFromObj(interp, objv[3], &radius) != TCL_OK) {
4627        return TCL_ERROR;
4628    }
4629    if (objc == 5) {
4630        const char *name = Tcl_GetString(objv[4]);
4631        g_renderer->setStreamlinesTypeToTubes(name, numSides, radius);
4632    } else {
4633        g_renderer->setStreamlinesTypeToTubes("all", numSides, radius);
4634    }
4635    return TCL_OK;
4636}
4637
4638static int
4639StreamlinesVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
4640                     Tcl_Obj *const *objv)
4641{
4642    bool state;
4643    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
4644        return TCL_ERROR;
4645    }
4646    if (objc == 4) {
4647        const char *name = Tcl_GetString(objv[3]);
4648        g_renderer->setStreamlinesVisibility(name, state);
4649    } else {
4650        g_renderer->setStreamlinesVisibility("all", state);
4651    }
4652    return TCL_OK;
4653}
4654
4655static Rappture::CmdSpec streamlinesOps[] = {
4656    {"add",       1, StreamlinesAddOp, 2, 3, "?dataSetName?"},
4657    {"ccolor",    1, StreamlinesColorOp, 5, 6, "r g b ?dataSetName?"},
4658    {"colormap",  7, StreamlinesColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
4659    {"colormode", 7, StreamlinesColorModeOp, 3, 4, "mode ?dataSetNme?"},
4660    {"delete",    1, StreamlinesDeleteOp, 2, 3, "?dataSetName?"},
4661    {"edges",     1, StreamlinesEdgeVisibilityOp, 3, 4, "bool ?dataSetName?"},
4662    {"length",    2, StreamlinesLengthOp, 3, 4, "length ?dataSetName?"},
4663    {"lighting",  3, StreamlinesLightingOp, 3, 4, "bool ?dataSetName?"},
4664    {"linecolor", 5, StreamlinesLineColorOp, 5, 6, "r g b ?dataSetName?"},
4665    {"lines",     5, StreamlinesLinesOp, 2, 3, "?dataSetName?"},
4666    {"linewidth", 5, StreamlinesLineWidthOp, 3, 4, "width ?dataSetName?"},
4667    {"opacity",   2, StreamlinesOpacityOp, 3, 4, "val ?dataSetName?"},
4668    {"orient",    2, StreamlinesOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
4669    {"pos",       1, StreamlinesPositionOp, 5, 6, "x y z ?dataSetName?"},
4670    {"ribbons",   1, StreamlinesRibbonsOp, 4, 5, "width angle ?dataSetName?"},
4671    {"scale",     2, StreamlinesScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
4672    {"seed",      2, StreamlinesSeedOp, 3, 14, "op params... ?dataSetName?"},
4673    {"tubes",     1, StreamlinesTubesOp, 4, 5, "numSides radius ?dataSetName?"},
4674    {"visible",   1, StreamlinesVisibleOp, 3, 4, "bool ?dataSetName?"}
4675};
4676static int nStreamlinesOps = NumCmdSpecs(streamlinesOps);
4677
4678static int
4679StreamlinesCmd(ClientData clientData, Tcl_Interp *interp, int objc,
4680               Tcl_Obj *const *objv)
4681{
4682    Tcl_ObjCmdProc *proc;
4683
4684    proc = Rappture::GetOpFromObj(interp, nStreamlinesOps, streamlinesOps,
4685                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
4686    if (proc == NULL) {
4687        return TCL_ERROR;
4688    }
4689    return (*proc) (clientData, interp, objc, objv);
4690}
4691
4692static int
4693VolumeAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
4694            Tcl_Obj *const *objv)
4695{
4696    if (objc == 3) {
4697        const char *name = Tcl_GetString(objv[2]);
4698        if (!g_renderer->addVolume(name)) {
4699            Tcl_AppendResult(interp, "Failed to create volume", (char*)NULL);
4700            return TCL_ERROR;
4701        }
4702    } else {
4703        if (!g_renderer->addVolume("all")) {
4704            Tcl_AppendResult(interp, "Failed to create volume for one or more data sets", (char*)NULL);
4705            return TCL_ERROR;
4706        }
4707    }
4708    return TCL_OK;
4709}
4710
4711static int
4712VolumeColorMapOp(ClientData clientData, Tcl_Interp *interp, int objc,
4713                 Tcl_Obj *const *objv)
4714{
4715    const char *colorMapName = Tcl_GetString(objv[2]);
4716    if (objc == 4) {
4717        const char *dataSetName = Tcl_GetString(objv[3]);
4718        g_renderer->setVolumeColorMap(dataSetName, colorMapName);
4719    } else {
4720        g_renderer->setVolumeColorMap("all", colorMapName);
4721    }
4722    return TCL_OK;
4723}
4724
4725static int
4726VolumeDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
4727               Tcl_Obj *const *objv)
4728{
4729    if (objc == 3) {
4730        const char *name = Tcl_GetString(objv[2]);
4731        g_renderer->deleteVolume(name);
4732    } else {
4733        g_renderer->deleteVolume("all");
4734    }
4735    return TCL_OK;
4736}
4737
4738static int
4739VolumeLightingOp(ClientData clientData, Tcl_Interp *interp, int objc,
4740                 Tcl_Obj *const *objv)
4741{
4742    bool state;
4743    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
4744        return TCL_ERROR;
4745    }
4746    if (objc == 4) {
4747        const char *name = Tcl_GetString(objv[3]);
4748        g_renderer->setVolumeLighting(name, state);
4749    } else {
4750        g_renderer->setVolumeLighting("all", state);
4751    }
4752    return TCL_OK;
4753}
4754
4755static int
4756VolumeOpacityOp(ClientData clientData, Tcl_Interp *interp, int objc,
4757                Tcl_Obj *const *objv)
4758{
4759    double opacity;
4760    if (Tcl_GetDoubleFromObj(interp, objv[2], &opacity) != TCL_OK) {
4761        return TCL_ERROR;
4762    }
4763    if (objc == 4) {
4764        const char *name = Tcl_GetString(objv[3]);
4765        g_renderer->setVolumeOpacity(name, opacity);
4766    } else {
4767        g_renderer->setVolumeOpacity("all", opacity);
4768    }
4769    return TCL_OK;
4770}
4771
4772static int
4773VolumeOrientOp(ClientData clientData, Tcl_Interp *interp, int objc,
4774               Tcl_Obj *const *objv)
4775{
4776    double quat[4];
4777    if (Tcl_GetDoubleFromObj(interp, objv[2], &quat[0]) != TCL_OK ||
4778        Tcl_GetDoubleFromObj(interp, objv[3], &quat[1]) != TCL_OK ||
4779        Tcl_GetDoubleFromObj(interp, objv[4], &quat[2]) != TCL_OK ||
4780        Tcl_GetDoubleFromObj(interp, objv[5], &quat[3]) != TCL_OK) {
4781        return TCL_ERROR;
4782    }
4783    if (objc == 7) {
4784        const char *name = Tcl_GetString(objv[6]);
4785        g_renderer->setVolumeOrientation(name, quat);
4786    } else {
4787        g_renderer->setVolumeOrientation("all", quat);
4788    }
4789    return TCL_OK;
4790}
4791
4792static int
4793VolumePositionOp(ClientData clientData, Tcl_Interp *interp, int objc,
4794                 Tcl_Obj *const *objv)
4795{
4796    double pos[3];
4797    if (Tcl_GetDoubleFromObj(interp, objv[2], &pos[0]) != TCL_OK ||
4798        Tcl_GetDoubleFromObj(interp, objv[3], &pos[1]) != TCL_OK ||
4799        Tcl_GetDoubleFromObj(interp, objv[4], &pos[2]) != TCL_OK) {
4800        return TCL_ERROR;
4801    }
4802    if (objc == 6) {
4803        const char *name = Tcl_GetString(objv[5]);
4804        g_renderer->setVolumePosition(name, pos);
4805    } else {
4806        g_renderer->setVolumePosition("all", pos);
4807    }
4808    return TCL_OK;
4809}
4810
4811static int
4812VolumeScaleOp(ClientData clientData, Tcl_Interp *interp, int objc,
4813              Tcl_Obj *const *objv)
4814{
4815    double scale[3];
4816    if (Tcl_GetDoubleFromObj(interp, objv[2], &scale[0]) != TCL_OK ||
4817        Tcl_GetDoubleFromObj(interp, objv[3], &scale[1]) != TCL_OK ||
4818        Tcl_GetDoubleFromObj(interp, objv[4], &scale[2]) != TCL_OK) {
4819        return TCL_ERROR;
4820    }
4821    if (objc == 6) {
4822        const char *name = Tcl_GetString(objv[5]);
4823        g_renderer->setVolumeScale(name, scale);
4824    } else {
4825        g_renderer->setVolumeScale("all", scale);
4826    }
4827    return TCL_OK;
4828}
4829
4830static int
4831VolumeShadingAmbientOp(ClientData clientData, Tcl_Interp *interp, int objc,
4832                       Tcl_Obj *const *objv)
4833{
4834    double coeff;
4835    if (Tcl_GetDoubleFromObj(interp, objv[3], &coeff) != TCL_OK) {
4836        return TCL_ERROR;
4837    }
4838
4839    if (objc == 5) {
4840        const char *name = Tcl_GetString(objv[4]);
4841        g_renderer->setVolumeAmbient(name, coeff);
4842    } else {
4843        g_renderer->setVolumeAmbient("all", coeff);
4844    }
4845    return TCL_OK;
4846}
4847
4848static int
4849VolumeShadingDiffuseOp(ClientData clientData, Tcl_Interp *interp, int objc,
4850                       Tcl_Obj *const *objv)
4851{
4852    double coeff;
4853    if (Tcl_GetDoubleFromObj(interp, objv[3], &coeff) != TCL_OK) {
4854        return TCL_ERROR;
4855    }
4856
4857    if (objc == 5) {
4858        const char *name = Tcl_GetString(objv[4]);
4859        g_renderer->setVolumeDiffuse(name, coeff);
4860    } else {
4861        g_renderer->setVolumeDiffuse("all", coeff);
4862    }
4863    return TCL_OK;
4864}
4865
4866static int
4867VolumeShadingSpecularOp(ClientData clientData, Tcl_Interp *interp, int objc,
4868                        Tcl_Obj *const *objv)
4869{
4870    double coeff, power;
4871    if (Tcl_GetDoubleFromObj(interp, objv[3], &coeff) != TCL_OK ||
4872        Tcl_GetDoubleFromObj(interp, objv[4], &power) != TCL_OK) {
4873        return TCL_ERROR;
4874    }
4875
4876    if (objc == 6) {
4877        const char *name = Tcl_GetString(objv[5]);
4878        g_renderer->setVolumeSpecular(name, coeff, power);
4879    } else {
4880        g_renderer->setVolumeSpecular("all", coeff, power);
4881    }
4882    return TCL_OK;
4883}
4884
4885static Rappture::CmdSpec volumeShadingOps[] = {
4886    {"ambient",  1, VolumeShadingAmbientOp, 4, 5, "coeff ?dataSetName?"},
4887    {"diffuse",  1, VolumeShadingDiffuseOp, 4, 5, "coeff ?dataSetName?"},
4888    {"specular", 1, VolumeShadingSpecularOp, 5, 6, "coeff power ?dataSetName?"}
4889};
4890static int nVolumeShadingOps = NumCmdSpecs(volumeShadingOps);
4891
4892static int
4893VolumeShadingOp(ClientData clientData, Tcl_Interp *interp, int objc,
4894                Tcl_Obj *const *objv)
4895{
4896    Tcl_ObjCmdProc *proc;
4897
4898    proc = Rappture::GetOpFromObj(interp, nVolumeShadingOps, volumeShadingOps,
4899                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
4900    if (proc == NULL) {
4901        return TCL_ERROR;
4902    }
4903    return (*proc) (clientData, interp, objc, objv);
4904}
4905
4906static int
4907VolumeVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
4908                Tcl_Obj *const *objv)
4909{
4910    bool state;
4911    if (GetBooleanFromObj(interp, objv[2], &state) != TCL_OK) {
4912        return TCL_ERROR;
4913    }
4914    if (objc == 4) {
4915        const char *name = Tcl_GetString(objv[3]);
4916        g_renderer->setVolumeVisibility(name, state);
4917    } else {
4918        g_renderer->setVolumeVisibility("all", state);
4919    }
4920    return TCL_OK;
4921}
4922
4923static Rappture::CmdSpec volumeOps[] = {
4924    {"add",      1, VolumeAddOp, 2, 3, "?dataSetName?"},
4925    {"colormap", 1, VolumeColorMapOp, 3, 4, "colorMapName ?dataSetName?"},
4926    {"delete",   1, VolumeDeleteOp, 2, 3, "?dataSetName?"},
4927    {"lighting", 1, VolumeLightingOp, 3, 4, "bool ?dataSetName?"},
4928    {"opacity",  2, VolumeOpacityOp, 3, 4, "val ?dataSetName?"},
4929    {"orient",   2, VolumeOrientOp, 6, 7, "qw qx qy qz ?dataSetName?"},
4930    {"pos",      1, VolumePositionOp, 5, 6, "x y z ?dataSetName?"},
4931    {"scale",    2, VolumeScaleOp, 5, 6, "sx sy sz ?dataSetName?"},
4932    {"shading",  2, VolumeShadingOp, 4, 6, "oper val ?dataSetName?"},
4933    {"visible",  1, VolumeVisibleOp, 3, 4, "bool ?dataSetName?"}
4934};
4935static int nVolumeOps = NumCmdSpecs(volumeOps);
4936
4937static int
4938VolumeCmd(ClientData clientData, Tcl_Interp *interp, int objc,
4939          Tcl_Obj *const *objv)
4940{
4941    Tcl_ObjCmdProc *proc;
4942
4943    proc = Rappture::GetOpFromObj(interp, nVolumeOps, volumeOps,
4944                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
4945    if (proc == NULL) {
4946        return TCL_ERROR;
4947    }
4948    return (*proc) (clientData, interp, objc, objv);
4949}
4950
4951/**
4952 * \brief Execute commands from client in Tcl interpreter
4953 */
4954int
4955Rappture::VtkVis::processCommands(Tcl_Interp *interp, FILE *fin, FILE *fout)
4956{
4957    Tcl_DString cmdbuffer;
4958    Tcl_DStringInit(&cmdbuffer);
4959
4960    int fdIn = fileno(fin);
4961    int fdOut = fileno(fout);
4962    int flags = fcntl(fdIn, F_GETFL, 0);
4963    fcntl(fdIn, F_SETFL, flags & ~O_NONBLOCK);
4964
4965    int status = TCL_OK;
4966    int nCommands = 0;
4967    bool isComplete = false;
4968    while ((!feof(fin)) && (status == TCL_OK)) {
4969        while (!feof(fin)) {
4970            int c = fgetc(fin);
4971            if (c <= 0) {
4972                if (errno == EWOULDBLOCK) {
4973                    break;
4974                }
4975                if (feof(fin))
4976                    return 1;
4977                else
4978                    return c;
4979            }
4980            char ch = (char)c;
4981            Tcl_DStringAppend(&cmdbuffer, &ch, 1);
4982            if (ch == '\n') {
4983                isComplete = Tcl_CommandComplete(Tcl_DStringValue(&cmdbuffer));
4984                if (isComplete) {
4985                    break;
4986                }
4987            }
4988        }
4989        // no command? then we're done for now
4990        if (Tcl_DStringLength(&cmdbuffer) == 0) {
4991            break;
4992        }
4993        if (isComplete) {
4994            // back to original flags during command evaluation...
4995            fcntl(fdIn, F_SETFL, flags & ~O_NONBLOCK);
4996            TRACE("command: '%s'", Tcl_DStringValue(&cmdbuffer));
4997            status = ExecuteCommand(interp, &cmdbuffer);
4998            // non-blocking for next read -- we might not get anything
4999            fcntl(fdIn, F_SETFL, flags | O_NONBLOCK);
5000            isComplete = false;
5001            nCommands++;
5002        }
5003    }
5004    fcntl(fdIn, F_SETFL, flags);
5005
5006    if (status != TCL_OK) {
5007        const char *string;
5008        int nBytes;
5009
5010        string = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
5011        TRACE("ERROR errorInfo=(%s)", string);
5012
5013        nBytes = strlen(string);
5014        struct iovec iov[3];
5015        iov[0].iov_base = (char *)"VtkVis Server Error: ";
5016        iov[0].iov_len = strlen((char *)iov[0].iov_base);
5017        iov[1].iov_base = (char *)string;
5018        iov[1].iov_len = nBytes;
5019        iov[2].iov_base = (char *)"\n";
5020        iov[2].iov_len = strlen((char *)iov[2].iov_base);
5021        if (writev(fdOut, iov, 3) < 0) {
5022            ERROR("write failed: %s", strerror(errno));
5023        }
5024        return 0;
5025    }
5026
5027    return 1;
5028}
5029
5030/**
5031 * \brief Create Tcl interpreter and add commands
5032 *
5033 * \return The initialized Tcl interpreter
5034 */
5035Tcl_Interp *
5036Rappture::VtkVis::initTcl()
5037{
5038    Tcl_Interp *interp;
5039    interp = Tcl_CreateInterp();
5040
5041    Tcl_MakeSafe(interp);
5042
5043    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
5044    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
5045    Tcl_CreateObjCommand(interp, "colormap",    ColorMapCmd,    NULL, NULL);
5046    Tcl_CreateObjCommand(interp, "contour2d",   Contour2DCmd,   NULL, NULL);
5047    Tcl_CreateObjCommand(interp, "contour3d",   Contour3DCmd,   NULL, NULL);
5048    Tcl_CreateObjCommand(interp, "dataset",     DataSetCmd,     NULL, NULL);
5049    Tcl_CreateObjCommand(interp, "glyphs",      GlyphsCmd,      NULL, NULL);
5050    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
5051    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
5052    Tcl_CreateObjCommand(interp, "lic",         LICCmd,         NULL, NULL);
5053    Tcl_CreateObjCommand(interp, "molecule",    MoleculeCmd,    NULL, NULL);
5054    Tcl_CreateObjCommand(interp, "polydata",    PolyDataCmd,    NULL, NULL);
5055    Tcl_CreateObjCommand(interp, "pseudocolor", PseudoColorCmd, NULL, NULL);
5056    Tcl_CreateObjCommand(interp, "renderer",    RendererCmd,    NULL, NULL);
5057    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
5058    Tcl_CreateObjCommand(interp, "streamlines", StreamlinesCmd, NULL, NULL);
5059    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
5060    return interp;
5061}
5062
5063/**
5064 * \brief Delete Tcl commands and interpreter
5065 *
5066 */
5067void Rappture::VtkVis::exitTcl(Tcl_Interp *interp)
5068{
5069
5070    Tcl_DeleteCommand(interp, "axis");
5071    Tcl_DeleteCommand(interp, "camera");
5072    Tcl_DeleteCommand(interp, "colormap");
5073    Tcl_DeleteCommand(interp, "contour2d");
5074    Tcl_DeleteCommand(interp, "contour3d");
5075    Tcl_DeleteCommand(interp, "dataset");
5076    Tcl_DeleteCommand(interp, "glyphs");
5077    Tcl_DeleteCommand(interp, "heightmap");
5078    Tcl_DeleteCommand(interp, "legend");
5079    Tcl_DeleteCommand(interp, "lic");
5080    Tcl_DeleteCommand(interp, "molecule");
5081    Tcl_DeleteCommand(interp, "polydata");
5082    Tcl_DeleteCommand(interp, "pseudocolor");
5083    Tcl_DeleteCommand(interp, "renderer");
5084    Tcl_DeleteCommand(interp, "screen");
5085    Tcl_DeleteCommand(interp, "streamlines");
5086    Tcl_DeleteCommand(interp, "volume");
5087
5088    Tcl_DeleteInterp(interp);
5089}
Note: See TracBrowser for help on using the repository browser.