source: branches/blt4/packages/vizservers/vtkvis/RpVtkRendererCmd.cpp @ 2742

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

sync with trunk

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