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

Last change on this file since 3106 was 3095, checked in by ldelgass, 12 years ago

Add ability to specify field used for orienting glyphs

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