source: branches/vtkvis_threaded/RpVtkRendererCmd.cpp @ 2483

Last change on this file since 2483 was 2483, checked in by gah, 13 years ago

initial changes for a threaded version of VtkVis? server.

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