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

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

cutplane now controls 3 axis aligned cutplanes. Add some comments to new
classes.

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