source: trunk/packages/vizservers/nanovis/FlowCmd.cpp @ 3936

Last change on this file since 3936 was 3936, checked in by ldelgass, 11 years ago

Fix raw DX (no <DX> header> path in flow data loader

  • Property svn:eol-style set to native
File size: 42.3 KB
RevLine 
[2798]1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
[3502]2/*
3 * Copyright (c) 2004-2013  HUBzero Foundation, LLC
4 *
5 */
[1429]6#include <assert.h>
[3559]7#define _OPEN_SYS
8#include <fcntl.h>
9#define _XOPEN_SOURCE_EXTENDED 1
10#include <sys/uio.h>
11#include <sys/stat.h>
[1429]12#include <stdlib.h>
13#include <stddef.h>
14#include <limits.h>
[1899]15#include <stdint.h>
[3559]16#include <unistd.h>
[1515]17#include <poll.h>
[3597]18
[1429]19#include <tcl.h>
[2831]20
[1429]21#include <RpOutcome.h>
[2831]22
[3492]23#include <vrmath/Vector3f.h>
24
[2831]25#include "nvconf.h"
26
[3605]27#include "nanovisServer.h"
[1429]28#include "nanovis.h"
[3567]29#include "CmdProc.h"
[3597]30#include "Command.h"
[3605]31#include "PPMWriter.h"
[3935]32#ifdef USE_VTK
33#include "VtkDataSetReader.h"
34#endif
35#include "VtkReader.h"
[2831]36#include "FlowCmd.h"
[3567]37#include "FlowTypes.h"
[3597]38#include "Flow.h"
[3567]39#include "FlowBox.h"
40#include "FlowParticles.h"
[2831]41#include "Switch.h"
42#include "TransferFunction.h"
[3611]43#include "LIC.h"
[1429]44#include "Unirect.h"
[2831]45#include "VelocityArrowsSlice.h"
[2877]46#include "Volume.h"
[3597]47#include "Trace.h"
[1429]48
[3605]49using namespace nv;
[3492]50using namespace vrmath;
51
[1429]52static Rappture::SwitchParseProc AxisSwitchProc;
53static Rappture::SwitchCustom axisSwitch = {
54    AxisSwitchProc, NULL, 0,
55};
56
57static Rappture::SwitchParseProc ColorSwitchProc;
58static Rappture::SwitchCustom colorSwitch = {
59    ColorSwitchProc, NULL, 0,
60};
61
62static Rappture::SwitchParseProc PointSwitchProc;
63static Rappture::SwitchCustom pointSwitch = {
64    PointSwitchProc, NULL, 0,
65};
66
67static Rappture::SwitchParseProc PositionSwitchProc;
68static Rappture::SwitchCustom positionSwitch = {
69    PositionSwitchProc, NULL, 0,
70};
71
72static Rappture::SwitchParseProc TransferFunctionSwitchProc;
73static Rappture::SwitchCustom transferFunctionSwitch = {
74    TransferFunctionSwitchProc, NULL, 0,
75};
76
[3597]77Rappture::SwitchSpec Flow::_switches[] = {
[3362]78    {Rappture::SWITCH_FLOAT, "-ambient", "value",
79     offsetof(FlowValues, ambient), 0},
[1493]80    {Rappture::SWITCH_BOOLEAN, "-arrows", "boolean",
[2875]81     offsetof(FlowValues, showArrows), 0},
[1429]82    {Rappture::SWITCH_CUSTOM, "-axis", "axis",
[2875]83     offsetof(FlowValues, slicePos.axis), 0, 0, &axisSwitch},
[1493]84    {Rappture::SWITCH_FLOAT, "-diffuse", "value",
[2875]85     offsetof(FlowValues, diffuse), 0},
[1429]86    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
[2875]87     offsetof(FlowValues, isHidden), 0},
[3397]88    {Rappture::SWITCH_BOOLEAN, "-light2side", "boolean",
89     offsetof(FlowValues, twoSidedLighting), 0},
[1493]90    {Rappture::SWITCH_FLOAT, "-opacity", "value",
[2875]91     offsetof(FlowValues, opacity), 0},
[1493]92    {Rappture::SWITCH_BOOLEAN, "-outline", "boolean",
[2875]93     offsetof(FlowValues, showOutline), 0},
[1429]94    {Rappture::SWITCH_CUSTOM, "-position", "number",
[2875]95     offsetof(FlowValues, slicePos), 0, 0, &positionSwitch},
[1493]96    {Rappture::SWITCH_BOOLEAN, "-slice", "boolean",
[2875]97     offsetof(FlowValues, sliceVisible), 0},
[3362]98    {Rappture::SWITCH_FLOAT, "-specularExp", "value",
99     offsetof(FlowValues, specularExp), 0},
100    {Rappture::SWITCH_FLOAT, "-specularLevel", "value",
[2875]101     offsetof(FlowValues, specular), 0},
[1429]102    {Rappture::SWITCH_CUSTOM, "-transferfunction", "name",
[3567]103     offsetof(FlowValues, transferFunction), 0, 0, &transferFunctionSwitch},
[1429]104    {Rappture::SWITCH_BOOLEAN, "-volume", "boolean",
[2875]105     offsetof(FlowValues, showVolume), 0},
[1429]106    {Rappture::SWITCH_END}
107};
108
109Rappture::SwitchSpec FlowParticles::_switches[] = {
[3630]110    {Rappture::SWITCH_CUSTOM, "-axis", "axis",
[2875]111     offsetof(FlowParticlesValues, position.axis), 0, 0, &axisSwitch},
[1429]112    {Rappture::SWITCH_CUSTOM, "-color", "{r g b a}",
[2875]113     offsetof(FlowParticlesValues, color), 0, 0,  &colorSwitch},
[1429]114    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
[2875]115     offsetof(FlowParticlesValues, isHidden), 0},
[1429]116    {Rappture::SWITCH_CUSTOM, "-position", "number",
[2875]117     offsetof(FlowParticlesValues, position), 0, 0, &positionSwitch},
[1497]118    {Rappture::SWITCH_FLOAT, "-size", "float",
[2875]119     offsetof(FlowParticlesValues, particleSize), 0},
[1429]120    {Rappture::SWITCH_END}
121};
122
123Rappture::SwitchSpec FlowBox::_switches[] = {
124    {Rappture::SWITCH_CUSTOM, "-color", "{r g b a}",
[2875]125     offsetof(FlowBoxValues, color), 0, 0,  &colorSwitch},
[1429]126    {Rappture::SWITCH_CUSTOM, "-corner1", "{x y z}",
[2875]127     offsetof(FlowBoxValues, corner1), 0, 0, &pointSwitch},
[1429]128    {Rappture::SWITCH_CUSTOM, "-corner2", "{x y z}",
[2875]129     offsetof(FlowBoxValues, corner2), 0, 0, &pointSwitch},
[1429]130    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
[2875]131     offsetof(FlowBoxValues, isHidden), 0},
[1429]132    {Rappture::SWITCH_FLOAT, "-linewidth", "number",
[2875]133     offsetof(FlowBoxValues, lineWidth), 0},
[1429]134    {Rappture::SWITCH_END}
135};
136
137static int
138FlowDataFileOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]139               Tcl_Obj *const *objv)
[1429]140{
141    Rappture::Outcome result;
[3564]142
[1429]143    const char *fileName;
144    fileName = Tcl_GetString(objv[3]);
[3564]145    TRACE("File: %s", fileName);
[1429]146
[1434]147    int nComponents;
148    if (Tcl_GetIntFromObj(interp, objv[4], &nComponents) != TCL_OK) {
[1429]149        return TCL_ERROR;
150    }
[1434]151    if ((nComponents < 1) || (nComponents > 4)) {
[2875]152        Tcl_AppendResult(interp, "bad # of components \"",
153                         Tcl_GetString(objv[4]), "\"", (char *)NULL);
154        return TCL_ERROR;
[1429]155    }
156    Rappture::Buffer buf;
157    if (!buf.load(result, fileName)) {
[2875]158        Tcl_AppendResult(interp, "can't load data from \"", fileName, "\": ",
159                         result.remark(), (char *)NULL);
160        return TCL_ERROR;
[1429]161    }
162
[1510]163    Rappture::Unirect3d *dataPtr;
164    dataPtr = new Rappture::Unirect3d(nComponents);
[3597]165    Flow *flow = (Flow *)clientData;
[1446]166    size_t length = buf.size();
167    char *bytes = (char *)buf.bytes();
168    if ((length > 4) && (strncmp(bytes, "<DX>", 4) == 0)) {
[2922]169        if (!dataPtr->importDx(result, nComponents, length-4, bytes+4)) {
[2875]170            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
171            delete dataPtr;
172            return TCL_ERROR;
173        }
[1462]174    } else if ((length > 10) && (strncmp(bytes, "unirect3d ", 10) == 0)) {
[2922]175        if (dataPtr->parseBuffer(interp, buf) != TCL_OK) {
[2875]176            delete dataPtr;
177            return TCL_ERROR;
178        }
[1462]179    } else if ((length > 10) && (strncmp(bytes, "unirect2d ", 10) == 0)) {
[2875]180        Rappture::Unirect2d *u2dPtr;
181        u2dPtr = new Rappture::Unirect2d(nComponents);
[2922]182        if (u2dPtr->parseBuffer(interp, buf) != TCL_OK) {
[2875]183            delete u2dPtr;
184            return TCL_ERROR;
185        }
[2922]186        dataPtr->convert(u2dPtr);
[2875]187        delete u2dPtr;
[1429]188    } else {
[3564]189        TRACE("header is %.14s", buf.bytes());
[2922]190        if (!dataPtr->importDx(result, nComponents, length, bytes)) {
[2875]191            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
192            delete dataPtr;
193            return TCL_ERROR;
194        }
[1429]195    }
[1510]196    if (dataPtr->nValues() == 0) {
[2875]197        delete dataPtr;
198        Tcl_AppendResult(interp, "no data found in \"", fileName, "\"",
199                         (char *)NULL);
200        return TCL_ERROR;
[1510]201    }
[3597]202    flow->data(dataPtr);
[3630]203    Flow::updatePending = true;
204    NanoVis::eventuallyRedraw();
[1429]205    return TCL_OK;
206}
207
[2875]208/**
[1434]209 * $flow data follows nbytes nComponents
[1431]210 */
[1429]211static int
212FlowDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]213                  Tcl_Obj *const *objv)
[1429]214{
215    Rappture::Outcome result;
216
[3564]217    TRACE("Enter");
218
[1429]219    int nBytes;
220    if (Tcl_GetIntFromObj(interp, objv[3], &nBytes) != TCL_OK) {
[3452]221        ERROR("Bad nBytes \"%s\"", Tcl_GetString(objv[3]));
[1429]222        return TCL_ERROR;
223    }
224    if (nBytes <= 0) {
[2875]225        Tcl_AppendResult(interp, "bad # bytes request \"",
226                         Tcl_GetString(objv[3]), "\" for \"data follows\"", (char *)NULL);
[3452]227        ERROR("Bad nbytes %d", nBytes);
[2875]228        return TCL_ERROR;
[1429]229    }
[1434]230    int nComponents;
231    if (Tcl_GetIntFromObj(interp, objv[4], &nComponents) != TCL_OK) {
[3452]232        ERROR("Bad # of components \"%s\"", Tcl_GetString(objv[4]));
[1429]233        return TCL_ERROR;
234    }
[1434]235    if (nComponents <= 0) {
[2875]236        Tcl_AppendResult(interp, "bad # of components request \"",
237                         Tcl_GetString(objv[4]), "\" for \"data follows\"", (char *)NULL);
[3452]238        ERROR("Bad # of components %d", nComponents);
[2875]239        return TCL_ERROR;
[1431]240    }
[3605]241    Rappture::Buffer buf(nBytes);
[3564]242    TRACE("Flow data loading bytes: %d components: %d", nBytes, nComponents);
[1429]243    if (GetDataStream(interp, buf, nBytes) != TCL_OK) {
244        return TCL_ERROR;
245    }
[3605]246    char *bytes = (char *)buf.bytes();
247    size_t length = buf.size();
248
[3935]249    Rappture::Unirect3d *unirect = NULL;
250    Volume *volume = NULL;
[1478]251
[3597]252    Flow *flow = (Flow *)clientData;
[1446]253    if ((length > 4) && (strncmp(bytes, "<DX>", 4) == 0)) {
[3935]254        unirect = new Rappture::Unirect3d(nComponents);
255        if (!unirect->importDx(result, nComponents, length - 4, bytes + 4)) {
[2875]256            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
[3935]257            delete unirect;
[2875]258            return TCL_ERROR;
259        }
[1462]260    } else if ((length > 10) && (strncmp(bytes, "unirect3d ", 10) == 0)) {
[3935]261        unirect = new Rappture::Unirect3d(nComponents);
262        if (unirect->parseBuffer(interp, buf) != TCL_OK) {
263            delete unirect;
[2875]264            return TCL_ERROR;
265        }
[1462]266    } else if ((length > 10) && (strncmp(bytes, "unirect2d ", 10) == 0)) {
[3935]267        unirect = new Rappture::Unirect3d(nComponents);
[2875]268        Rappture::Unirect2d *u2dPtr;
269        u2dPtr = new Rappture::Unirect2d(nComponents);
[2922]270        if (u2dPtr->parseBuffer(interp, buf) != TCL_OK) {
[3936]271            delete unirect;
[2875]272            delete u2dPtr;
273            return TCL_ERROR;
274        }
[3935]275        unirect->convert(u2dPtr);
[2875]276        delete u2dPtr;
[3630]277    } else if ((length > 14) && (strncmp(bytes, "# vtk DataFile", 14) == 0)) {
278        TRACE("VTK loading...");
279        std::stringstream fdata;
280        fdata.write(bytes, length);
281        if (length <= 0) {
282            ERROR("data buffer is empty");
283            abort();
284        }
285        Rappture::Outcome context;
[3935]286#ifdef USE_VTK
287        volume = load_vtk_volume_stream(context, flow->name(), bytes, length);
288#else
289        std::stringstream fdata;
290        fdata.write(bytes, nBytes);
291        volume = load_vtk_volume_stream(context, flow->name(), fdata);
292#endif
[3630]293        if (volume == NULL) {
294            Tcl_AppendResult(interp, context.remark(), (char*)NULL);
295            return TCL_ERROR;
296        }
[1429]297    } else {
[3564]298        TRACE("header is %.14s", buf.bytes());
[3936]299        unirect = new Rappture::Unirect3d(nComponents);
[3935]300        if (!unirect->importDx(result, nComponents, length, bytes)) {
[2875]301            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
[3935]302            delete unirect;
[2875]303            return TCL_ERROR;
304        }
[1429]305    }
[3935]306    if (unirect != NULL && unirect->nValues() == 0) {
307        delete unirect;
[2875]308        Tcl_AppendResult(interp, "no data found in stream", (char *)NULL);
309        return TCL_ERROR;
[1510]310    }
[3935]311#if 0
[3565]312    TRACE("nx = %d ny = %d nz = %d",
[3935]313          unirect->xNum(), unirect->yNum(), unirect->zNum());
[3565]314    TRACE("x0 = %lg y0 = %lg z0 = %lg",
[3935]315          unirect->xMin(), unirect->yMin(), unirect->zMin());
[3565]316    TRACE("lx = %lg ly = %lg lz = %lg",
[3935]317          unirect->xMax() - unirect->xMin(),
318          unirect->yMax() - unirect->yMin(),
319          unirect->zMax() - unirect->zMin());
[3565]320    TRACE("dx = %lg dy = %lg dz = %lg",
[3935]321          unirect->xNum() > 1 ? (unirect->xMax() - unirect->xMin())/(unirect->xNum()-1) : 0,
322          unirect->yNum() > 1 ? (unirect->yMax() - unirect->yMin())/(unirect->yNum()-1) : 0,
323          unirect->zNum() > 1 ? (unirect->zMax() - unirect->zMin())/(unirect->zNum()-1) : 0);
[3565]324    TRACE("magMin = %lg magMax = %lg",
[3935]325          unirect->magMin(), unirect->magMax());
326#endif
327    if (unirect != NULL) {
328        flow->data(unirect);
329    } else {
330        flow->data(volume);
331    }
332    double range[2];
333    flow->getVectorRange(range);
[1431]334    {
335        char info[1024];
[3605]336        int length =
337            sprintf(info, "nv>data tag %s min %g max %g\n",
[3935]338                    flow->name(), range[0], range[1]);
[3605]339#ifdef USE_THREADS
340        queueResponse(info, (size_t)length, Response::VOLATILE);
341#else
342        if (SocketWrite(info, (size_t)length) < 0) {
343            return TCL_ERROR;
344        }
345#endif
[1431]346    }
[3630]347    Flow::updatePending = true;
348    NanoVis::eventuallyRedraw();
[1429]349    return TCL_OK;
350}
351
352static Rappture::CmdSpec flowDataOps[] = {
[1434]353    {"file",    2, FlowDataFileOp,    5, 5, "fileName nComponents",},
354    {"follows", 2, FlowDataFollowsOp, 5, 5, "size nComponents",},
[1429]355};
356static int nFlowDataOps = NumCmdSpecs(flowDataOps);
357
358static int
359FlowDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]360           Tcl_Obj *const *objv)
[1429]361{
362    Tcl_ObjCmdProc *proc;
363
364    proc = Rappture::GetOpFromObj(interp, nFlowDataOps, flowDataOps,
365                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
366    if (proc == NULL) {
367        return TCL_ERROR;
368    }
369    return (*proc) (clientData, interp, objc, objv);
370}
371
[3567]372/**
[2875]373 * \brief Convert a Tcl_Obj representing the label of a child node into its
374 * integer node id.
[1429]375 *
[2875]376 * \param clientData Flag indicating if the node is considered before or
377 * after the insertion position.
378 * \param interp Interpreter to send results back to
379 * \param switchName Not used
380 * \param objPtr String representation
381 * \param record Structure record
382 * \param offset Not used
383 * \param flags Not used
[1429]384 *
[2875]385 * \return The return value is a standard Tcl result.
[1429]386 */
387static int
[2875]388AxisSwitchProc(ClientData clientData, Tcl_Interp *interp,
389               const char *switchName, Tcl_Obj *objPtr,
390               char *record, int offset, int flags)
[1429]391{
392    const char *string = Tcl_GetString(objPtr);
393    if (string[1] == '\0') {
[3630]394        FlowSliceAxis *axisPtr = (FlowSliceAxis *)(record + offset);
[1429]395        char c;
396        c = tolower((unsigned char)string[0]);
397        if (c == 'x') {
[3630]398            *axisPtr = AXIS_X;
[1429]399            return TCL_OK;
400        } else if (c == 'y') {
[3630]401            *axisPtr = AXIS_Y;
[1429]402            return TCL_OK;
403        } else if (c == 'z') {
[3630]404            *axisPtr = AXIS_Z;
[1429]405            return TCL_OK;
406        }
407        /*FALLTHRU*/
408    }
409    Tcl_AppendResult(interp, "bad axis \"", string,
410                     "\": should be x, y, or z", (char*)NULL);
411    return TCL_ERROR;
412}
413
[2875]414/**
415 * \brief Convert a Tcl_Obj representing the label of a list of four color
416 * components in to a RGBA color value.
[1429]417 *
[2875]418 * \param clientData Flag indicating if the node is considered before or
419 * after the insertion position.
420 * \param interp Interpreter to send results back to
421 * \param switchName Not used
422 * \param objPtr String representation
423 * \param record Structure record
424 * \param offset Not used
425 * \param flags Not used
426 *
427 * \return The return value is a standard Tcl result.
[1429]428 */
429static int
[2875]430ColorSwitchProc(ClientData clientData, Tcl_Interp *interp,
431                const char *switchName, Tcl_Obj *objPtr,
432                char *record, int offset, int flags)
[1429]433{
434    Tcl_Obj **objv;
435    int objc;
[3567]436    FlowColor *color = (FlowColor *)(record + offset);
[1429]437
438    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
[2875]439        return TCL_ERROR;
[1429]440    }
441    if ((objc < 3) || (objc > 4)) {
[2875]442        Tcl_AppendResult(interp, "wrong # of elements in color definition",
443                         (char *)NULL);
444        return TCL_ERROR;
[1429]445    }
446    float values[4];
447    int i;
448    values[3] = 1.0f;
449    for (i = 0; i < objc; i++) {
[2875]450        float value;
[1429]451
[2875]452        if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
453            return TCL_ERROR;
454        }
455        if ((value < 0.0) || (value > 1.0)) {
456            Tcl_AppendResult(interp, "bad component value in \"",
457                             Tcl_GetString(objPtr), "\": color values must be [0..1]",
458                             (char *)NULL);
459            return TCL_ERROR;
460        }
461        values[i] = value;
462    }
[3567]463    color->r = values[0];
464    color->g = values[1];
465    color->b = values[2];
466    color->a = values[3];
[1429]467    return TCL_OK;
468}
469
[2875]470/**
471 * \brief Convert a Tcl_Obj representing the a 3-D coordinate into a point.
[1429]472 *
[2875]473 * \param clientData Flag indicating if the node is considered before or
474 * after the insertion position.
475 * \param interp Interpreter to send results back to
476 * \param switchName Not used
477 * \param objPtr String representation
478 * \param record Structure record
479 * \param offset Not used
480 * \param flags Not used
[1429]481 *
[2875]482 * \return The return value is a standard Tcl result.
[1429]483 */
484static int
[2875]485PointSwitchProc(ClientData clientData, Tcl_Interp *interp,
486                const char *switchName, Tcl_Obj *objPtr,
487                char *record, int offset, int flags)
[1429]488{
[3567]489    FlowPoint *point = (FlowPoint *)(record + offset);
[1429]490    int objc;
491    Tcl_Obj **objv;
492
493    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
[2875]494        return TCL_ERROR;
[1429]495    }
496    if (objc != 3) {
[2875]497        Tcl_AppendResult(interp, "wrong # of elements for box coordinates: "
498                         " should be \"x y z\"", (char *)NULL);
499        return TCL_ERROR;
[1429]500    }
501    float values[3];
502    int i;
503    for (i = 0; i < objc; i++) {
[2875]504        float value;
[1429]505
[2875]506        if (GetFloatFromObj(interp, objv[i], &value) != TCL_OK) {
507            return TCL_ERROR;
508        }
509        values[i] = value;
510    }
[3567]511    point->x = values[0];
512    point->y = values[1];
513    point->z = values[2];
[1429]514    return TCL_OK;
515}
516
[2875]517/**
518 * \brief Convert a Tcl_Obj representing the a 3-D coordinate into a point.
[1429]519 *
[2875]520 * \param clientData Flag indicating if the node is considered before or
521 * after the insertion position.
522 * \param interp Interpreter to send results back to
523 * \param switchName Not used
524 * \param objPtr String representation
525 * \param record Structure record
526 * \param offset Not used
527 * \param flags Not used
[1429]528 *
[2875]529 * \return The return value is a standard Tcl result.
[1429]530 */
531static int
[2875]532PositionSwitchProc(ClientData clientData, Tcl_Interp *interp,
533                   const char *switchName, Tcl_Obj *objPtr,
534                   char *record, int offset, int flags)
[1429]535{
536    FlowPosition *posPtr = (FlowPosition *)(record + offset);
537    const char *string;
538    char *p;
539
540    string = Tcl_GetString(objPtr);
[1571]541    p = strrchr((char *)string, '%');
[1429]542    if (p == NULL) {
[2875]543        float value;
[1429]544
[2875]545        if (GetFloatFromObj(interp, objPtr, &value) != TCL_OK) {
546            return TCL_ERROR;
547        }
548        posPtr->value = value;
549        posPtr->flags = ABSPOS;
[1429]550    } else {
[2875]551        double value;
[1429]552
[2875]553        *p = '\0';
554        if (Tcl_GetDouble(interp, string, &value) != TCL_OK) {
555            return TCL_ERROR;
556        }
557        posPtr->value = (float)value * 0.01;
558        posPtr->flags = RELPOS;
[1429]559    }
560    return TCL_OK;
561}
562
[2875]563/**
564 * \brief Convert a Tcl_Obj representing the transfer function into a
565 *  TransferFunction pointer. 
[1429]566 *
[2875]567 * The transfer function must have been previously defined.
[1429]568 *
[2875]569 * \param clientData Flag indicating if the node is considered before or
570 * after the insertion position.
571 * \param interp Interpreter to send results back to
572 * \param switchName Not used
573 * \param objPtr String representation
574 * \param record Structure record
575 * \param offset Not used
576 * \param flags Not used
[1429]577 *
[2875]578 * \return The return value is a standard Tcl result.
[1429]579 */
580static int
[2875]581TransferFunctionSwitchProc(ClientData clientData, Tcl_Interp *interp,
582                           const char *switchName, Tcl_Obj *objPtr,
583                           char *record, int offset, int flags)
[1429]584{
585    TransferFunction **funcPtrPtr = (TransferFunction **)(record + offset);
[3567]586    TransferFunction *tf = NanoVis::getTransferFunction(Tcl_GetString(objPtr));
587    if (tf == NULL) {
[1429]588        Tcl_AppendResult(interp, "transfer function \"", Tcl_GetString(objPtr),
589                         "\" is not defined", (char*)NULL);
590        return TCL_ERROR;
591    }
[3567]592    *funcPtrPtr = tf;
[1429]593    return TCL_OK;
594}
595
596static int
597FlowConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]598                Tcl_Obj *const *objv)
[1429]599{
[3597]600    Flow *flow = (Flow *)clientData;
[1429]601
[3597]602    if (flow->parseSwitches(interp, objc - 2, objv + 2) != TCL_OK) {
[2875]603        return TCL_ERROR;
[1429]604    }
[3630]605    if (flow->configure()) {
606        Flow::updatePending = true;
607    }
608    NanoVis::eventuallyRedraw();
[1429]609    return TCL_OK;
610}
611
612static int
613FlowParticlesAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]614                   Tcl_Obj *const *objv)
[1429]615{
[3597]616    Flow *flow = (Flow *)clientData;
[3567]617    const char *particlesName = Tcl_GetString(objv[3]);
618    FlowParticles *particles = flow->createParticles(particlesName);
619    if (particles == NULL) {
620        Tcl_AppendResult(interp, "Flow particle injection plane \"",
621                         particlesName,
622                         "\" already exists or could not be created",
623                         (char*)NULL);
[2875]624        return TCL_ERROR;
[1429]625    }
[3567]626    if (particles->parseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
627        flow->deleteParticles(particlesName);
[2875]628        return TCL_ERROR;
[1429]629    }
[3567]630    particles->configure();
[3630]631    Flow::updatePending = true;
[2877]632    NanoVis::eventuallyRedraw();
[1429]633    Tcl_SetObjResult(interp, objv[3]);
634    return TCL_OK;
635}
636
637static int
638FlowParticlesConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]639                         Tcl_Obj *const *objv)
[1429]640{
[3597]641    Flow *flow = (Flow *)clientData;
[3567]642    const char *particlesName = Tcl_GetString(objv[3]);
643    FlowParticles *particles = flow->getParticles(particlesName);
644    if (particles == NULL) {
645        Tcl_AppendResult(interp, "Flow particle injection plane \"",
646                         particlesName, "\" not found",
647                         (char*)NULL);
[2875]648        return TCL_ERROR;
[1429]649    }
[3567]650    if (particles->parseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
[2875]651        return TCL_ERROR;
[1429]652    }
[3630]653    if (particles->configure()) {
654        Flow::updatePending = true;
655    }
656    NanoVis::eventuallyRedraw();
[1429]657    return TCL_OK;
658}
659
660static int
661FlowParticlesDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]662                      Tcl_Obj *const *objv)
[1429]663{
[3597]664    Flow *flow = (Flow *)clientData;
[3567]665    for (int i = 3; i < objc; i++) {
666        flow->deleteParticles(Tcl_GetString(objv[i]));
[1429]667    }
[2877]668    NanoVis::eventuallyRedraw();
[1429]669    return TCL_OK;
670}
671
672static int
673FlowParticlesNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]674                     Tcl_Obj *const *objv)
[1429]675{
[3597]676    Flow *flow = (Flow *)clientData;
[1429]677    Tcl_Obj *listObjPtr;
678    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
[3567]679    std::vector<std::string> names;
680    flow->getParticlesNames(names);
681    for (std::vector<std::string>::iterator itr = names.begin();
682         itr != names.end(); ++itr) {
683        Tcl_Obj *objPtr = Tcl_NewStringObj(itr->c_str(), -1);
[2875]684        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
[1429]685    }
686    Tcl_SetObjResult(interp, listObjPtr);
687    return TCL_OK;
688}
689
690static Rappture::CmdSpec flowParticlesOps[] = {
691    {"add",        1, FlowParticlesAddOp,        4, 0, "name ?switches?",},
692    {"configure",  1, FlowParticlesConfigureOp,  4, 0, "name ?switches?",},
[3567]693    {"delete",     1, FlowParticlesDeleteOp,     4, 0, "name ?name...?"},
694    {"names",      1, FlowParticlesNamesOp,      3, 3, ""},
[1429]695};
696
697static int nFlowParticlesOps = NumCmdSpecs(flowParticlesOps);
698
[2875]699/**
700 * \brief This procedure is invoked to process commands on behalf of the flow
701 * object.
702 *
703 * Side effects: See the user documentation.
704 *
705 * $flow particles oper $name
706 *
707 * \return A standard Tcl result.
708 */
[1429]709static int
710FlowParticlesOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]711                Tcl_Obj *const *objv)
[1429]712{
713    Tcl_ObjCmdProc *proc;
714    proc = Rappture::GetOpFromObj(interp, nFlowParticlesOps, flowParticlesOps,
[2875]715                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
[1429]716    if (proc == NULL) {
[2875]717        return TCL_ERROR;
[1429]718    }
[3597]719    Flow *flow = (Flow *)clientData;
720    Tcl_Preserve(flow);
[1429]721    int result;
722    result = (*proc) (clientData, interp, objc, objv);
[3597]723    Tcl_Release(flow);
[1429]724    return result;
725}
726
727static int
728FlowBoxAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]729             Tcl_Obj *const *objv)
[1429]730{
[3597]731    Flow *flow = (Flow *)clientData;
[3567]732    const char *boxName = Tcl_GetString(objv[3]);
733    FlowBox *box = flow->createBox(boxName);
734    if (box == NULL) {
735        Tcl_AppendResult(interp, "Flow box \"", boxName,
736                         "\" already exists or could not be created",
737                         (char*)NULL);
[2875]738        return TCL_ERROR;
[1429]739    }
[3567]740    if (box->parseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
741        flow->deleteBox(boxName);
[2875]742        return TCL_ERROR;
[1429]743    }
[3567]744    NanoVis::eventuallyRedraw();
745    Tcl_SetObjResult(interp, objv[3]);
746    return TCL_OK;
747}
748
749static int
750FlowBoxConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
751                   Tcl_Obj *const *objv)
752{
[3597]753    Flow *flow = (Flow *)clientData;
[3567]754    const char *boxName = Tcl_GetString(objv[3]);
755    FlowBox *box = flow->getBox(boxName);
756    if (box == NULL) {
757        Tcl_AppendResult(interp, "Flow box \"", boxName, "\" not found",
758                         (char*)NULL);
[2875]759        return TCL_ERROR;
[1429]760    }
[3567]761    if (box->parseSwitches(interp, objc - 4, objv + 4) != TCL_OK) {
762        return TCL_ERROR;
763    }
[2877]764    NanoVis::eventuallyRedraw();
[1429]765    return TCL_OK;
766}
767
768static int
769FlowBoxDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]770                Tcl_Obj *const *objv)
[1429]771{
[3597]772    Flow *flow = (Flow *)clientData;
[3567]773    for (int i = 3; i < objc; i++) {
774        flow->deleteBox(Tcl_GetString(objv[i]));
[1429]775    }
[2877]776    NanoVis::eventuallyRedraw();
[1429]777    return TCL_OK;
778}
779
780static int
781FlowBoxNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]782               Tcl_Obj *const *objv)
[1429]783{
[3597]784    Flow *flow = (Flow *)clientData;
[3567]785    Tcl_Obj *listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
786    std::vector<std::string> names;
787    flow->getBoxNames(names);
788    for (std::vector<std::string>::iterator itr = names.begin();
789         itr != names.end(); ++itr) {
790        Tcl_Obj *objPtr = Tcl_NewStringObj(itr->c_str(), -1);
[2875]791        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
[1429]792    }
793    Tcl_SetObjResult(interp, listObjPtr);
794    return TCL_OK;
795}
796
797static Rappture::CmdSpec flowBoxOps[] = {
798    {"add",        1, FlowBoxAddOp,        4, 0, "name ?switches?",},
799    {"configure",  1, FlowBoxConfigureOp,  4, 0, "name ?switches?",},
[3567]800    {"delete",     1, FlowBoxDeleteOp,     4, 0, "name ?name...?"},
801    {"names",      1, FlowBoxNamesOp,      3, 3, ""},
[1429]802};
803
804static int nFlowBoxOps = NumCmdSpecs(flowBoxOps);
805
[2875]806/**
807 * \brief This procedure is invoked to process commands on behalf of the flow
808 *         object.
809 *
810 * Side effects:  See the user documentation.
811 *
812 * \return A standard Tcl result.
813 */
[1429]814static int
815FlowBoxOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]816          Tcl_Obj *const *objv)
[1429]817{
818    Tcl_ObjCmdProc *proc;
819    proc = Rappture::GetOpFromObj(interp, nFlowBoxOps, flowBoxOps,
[2875]820                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
[1429]821    if (proc == NULL) {
[2875]822        return TCL_ERROR;
[1429]823    }
[3597]824    Flow *flow = (Flow *)clientData;
825    Tcl_Preserve(flow);
[1429]826    int result;
827    result = (*proc) (clientData, interp, objc, objv);
[3597]828    Tcl_Release(flow);
[1429]829    return result;
830}
831
832/*
[1474]833 * CLIENT COMMAND:
834 *   $flow legend <width> <height>
835 *
836 * Clients use this to generate a legend image for the specified
837 * transfer function.  The legend image is a color gradient from 0
838 * to one, drawn in the given transfer function.  The resulting image
839 * is returned in the size <width> x <height>.
840 */
841static int
842FlowLegendOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]843             Tcl_Obj *const *objv)
[1474]844{
[3597]845    Flow *flow = (Flow *)clientData;
[1474]846   
847    const char *string = Tcl_GetString(objv[1]);
848    TransferFunction *tf;
[3597]849    tf = flow->getTransferFunction();
[1474]850    if (tf == NULL) {
851        Tcl_AppendResult(interp, "unknown transfer function \"", string, "\"",
[2875]852                         (char*)NULL);
[1474]853        return TCL_ERROR;
854    }
855    const char *label;
856    label = Tcl_GetString(objv[0]);
857    int w, h;
858    if ((Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK) ||
859        (Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK)) {
860        return TCL_ERROR;
861    }
[3630]862    if (Flow::updatePending) {
[3935]863        NanoVis::setFlowRanges();
[1474]864    }
[3630]865    NanoVis::renderLegend(tf, Flow::magMin, Flow::magMax, w, h, label);
[1474]866    return TCL_OK;
867}
868
[1429]869static Rappture::CmdSpec flowInstOps[] = {
870    {"box",         1, FlowBoxOp,        2, 0, "oper ?args?"},
871    {"configure",   1, FlowConfigureOp,  2, 0, "?switches?"},
[2875]872    {"data",        1, FlowDataOp,       2, 0, "oper ?args?"},
[1474]873    {"legend",      1, FlowLegendOp,     4, 4, "w h"},
[1429]874    {"particles",   1, FlowParticlesOp,  2, 0, "oper ?args?"}
875};
876static int nFlowInstOps = NumCmdSpecs(flowInstOps);
877
[2875]878/**
879 * \brief This procedure is invoked to process commands on behalf of the flow
880 * object.
881 *
882 * Side effects: See the user documentation.
883 *
884 * \return A standard Tcl result.
885 */
[3597]886int
[3611]887nv::FlowInstObjCmd(ClientData clientData, Tcl_Interp *interp, int objc,
888                   Tcl_Obj *const *objv)
[1429]889{
890    Tcl_ObjCmdProc *proc;
891    proc = Rappture::GetOpFromObj(interp, nFlowInstOps, flowInstOps,
[2875]892                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
[1429]893    if (proc == NULL) {
[2875]894        return TCL_ERROR;
[1429]895    }
896    assert(CheckGL(AT));
[3597]897    Flow *flow = (Flow *)clientData;
[3567]898    Tcl_Preserve(flow);
899    int result = (*proc) (clientData, interp, objc, objv);
900    Tcl_Release(flow);
[1429]901    return result;
902}
903
[2875]904/**
[3567]905 * \brief Deletes the command associated with the flow
[1429]906 *
[3567]907 * This is called only when the command associated with the flow is destroyed.
[1429]908 */
[3597]909void
[3611]910nv::FlowInstDeleteProc(ClientData clientData)
[1429]911{
[3597]912    Flow *flow = (Flow *)clientData;
[3567]913    NanoVis::deleteFlow(flow->name());
[1429]914}
915
916static int
917FlowAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]918          Tcl_Obj *const *objv)
[1429]919{
[3567]920    const char *name = Tcl_GetString(objv[2]);
921    Tcl_CmdInfo cmdInfo;
922    if (Tcl_GetCommandInfo(interp, name, &cmdInfo)) {
923        Tcl_AppendResult(interp, "an another command \"", name,
924                         "\" already exists.", (char *)NULL);
[3576]925        return TCL_ERROR;
[1429]926    }
[3597]927    Flow *flow = NanoVis::createFlow(interp, name);
[3567]928    if (flow == NULL) {
929        Tcl_AppendResult(interp, "Flow \"", name, "\" already exists",
930                         (char*)NULL);
[2875]931        return TCL_ERROR;
[1429]932    }
[3567]933    if (flow->parseSwitches(interp, objc - 3, objv + 3) != TCL_OK) {
934        Tcl_DeleteCommand(interp, flow->name());
[2875]935        return TCL_ERROR;
[1429]936    }
937    Tcl_SetObjResult(interp, objv[2]);
[2877]938    NanoVis::eventuallyRedraw();
[1429]939    return TCL_OK;
940}
941
942static int
943FlowDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]944             Tcl_Obj *const *objv)
[1429]945{
[3567]946    for (int i = 2; i < objc; i++) {
[3597]947        Flow *flow = NanoVis::getFlow(Tcl_GetString(objv[i]));
[3567]948        if (flow != NULL) {
949            Tcl_DeleteCommandFromToken(interp, flow->getCommandToken());
[2875]950        }
[1429]951    }
[3630]952    Flow::updatePending = true;
953    NanoVis::eventuallyRedraw();
[1429]954    return TCL_OK;
955}
956
[1431]957static int
958FlowExistsOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]959             Tcl_Obj *const *objv)
[1431]960{
[3567]961    bool value = false;
[3597]962    Flow *flow = NanoVis::getFlow(Tcl_GetString(objv[2]));
[3567]963    if (flow != NULL) {
[2875]964        value = true;
[1431]965    }
966    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), (int)value);
967    return TCL_OK;
968}
969
[2875]970/**
971 * \brief flow goto number
[1467]972 */
973static int
974FlowGotoOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]975           Tcl_Obj *const *objv)
[1467]976{
977    int nSteps;
978    if (Tcl_GetIntFromObj(interp, objv[2], &nSteps) != TCL_OK) {
979        return TCL_ERROR;
980    }
981    if ((nSteps < 0) || (nSteps > SHRT_MAX)) {
[2875]982        Tcl_AppendResult(interp, "flow goto: bad # of steps \"",
983                         Tcl_GetString(objv[2]), "\"", (char *)NULL);
984        return TCL_ERROR;
[1467]985    }
[3566]986    NanoVis::resetFlows();
[3630]987    if (Flow::updatePending) {
[3935]988        NanoVis::setFlowRanges();
[1467]989    }
[3567]990    for (int i = 0; i < nSteps; i++) {
[3630]991        NanoVis::licRenderer->convolve();
[3566]992        NanoVis::advectFlows();
[1467]993    }
[2877]994    NanoVis::eventuallyRedraw();
[1467]995    return TCL_OK;
996}
997
[1429]998static int
999FlowNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]1000            Tcl_Obj *const *objv)
[1429]1001{
1002    Tcl_Obj *listObjPtr;
1003    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
[3567]1004    for (NanoVis::FlowHashmap::iterator itr = NanoVis::flowTable.begin();
1005         itr != NanoVis::flowTable.end(); ++itr) {
[3597]1006        Flow *flow = itr->second;
[3567]1007        Tcl_Obj *objPtr = Tcl_NewStringObj(flow->name(), -1);
[2875]1008        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
[1429]1009    }
1010    Tcl_SetObjResult(interp, listObjPtr);
1011    return TCL_OK;
1012}
1013
1014static int
1015FlowNextOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]1016           Tcl_Obj *const *objv)
[1429]1017{
1018    assert(NanoVis::licRenderer != NULL);
[3630]1019    if (Flow::updatePending) {
[3935]1020        NanoVis::setFlowRanges();
[1429]1021    }
1022    NanoVis::licRenderer->convolve();
[3566]1023    NanoVis::advectFlows();
[3630]1024    NanoVis::eventuallyRedraw();
[1429]1025    return TCL_OK;
1026}
1027
1028static int
1029FlowResetOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]1030            Tcl_Obj *const *objv)
[1429]1031{
[3566]1032    NanoVis::resetFlows();
[1429]1033    return TCL_OK;
1034}
1035
[3598]1036#ifdef HAVE_FFMPEG
1037
[2875]1038/**
1039 * \brief Convert a Tcl_Obj representing the video format into its
1040 * integer id.
[2070]1041 *
[2875]1042 * \param clientData Not used
1043 * \param interp Interpreter to send results back to
1044 * \param switchName Not used
1045 * \param objPtr String representation
1046 * \param record Structure record
1047 * \param offset Not used
1048 * \param flags Not used
[2070]1049 *
[2875]1050 * \return The return value is a standard Tcl result.
[2070]1051 */
1052static int
[2875]1053VideoFormatSwitchProc(ClientData clientData, Tcl_Interp *interp,
1054                      const char *switchName, Tcl_Obj *objPtr,
1055                      char *record, int offset, int flags)
[2070]1056{
[3559]1057    Tcl_Obj **formatObjPtr = (Tcl_Obj **)(record + offset);
1058    Tcl_Obj *fmtObjPtr;
[2070]1059    const char *string;
1060    char c;
[3559]1061    int length;
[2070]1062
[3559]1063    string = Tcl_GetStringFromObj(objPtr, &length);
[2070]1064    c = string[0];
[3559]1065    if ((c == 'm') && (length > 1) &&
1066        (strncmp(string, "mpeg", length) == 0)) {
1067        fmtObjPtr =  Tcl_NewStringObj("mpeg1video", 10);
1068    } else if ((c == 't') && (strncmp(string, "theora", length) == 0)) {
1069        fmtObjPtr =  Tcl_NewStringObj("theora", 6);
1070    } else if ((c == 'm') && (length > 1) &&
1071               (strncmp(string, "mov", length) == 0)) {
1072        fmtObjPtr =  Tcl_NewStringObj("mov", 3);
[2070]1073    } else {
[2875]1074        Tcl_AppendResult(interp, "bad video format \"", string,
1075                         "\": should be mpeg, theora, or mov", (char*)NULL);
[3559]1076        return TCL_ERROR;
[2070]1077    }
[3559]1078    if (*formatObjPtr != NULL) {
1079        Tcl_DecrRefCount(*formatObjPtr);
1080    }
1081    Tcl_IncrRefCount(fmtObjPtr);
1082    *formatObjPtr = fmtObjPtr;
1083    return TCL_OK;
[2070]1084}
1085
[3559]1086struct FlowVideoSwitches {
[2875]1087    float frameRate;         /**< Frame rate */
1088    int bitRate;             /**< Video bitrate */
1089    int width, height;       /**< Dimensions of video frame. */
[3559]1090    int numFrames;
1091    Tcl_Obj *formatObjPtr;
[1515]1092};
1093
1094static Rappture::SwitchParseProc VideoFormatSwitchProc;
1095static Rappture::SwitchCustom videoFormatSwitch = {
1096    VideoFormatSwitchProc, NULL, 0,
1097};
1098
[3597]1099static Rappture::SwitchSpec flowVideoSwitches[] = {
[2810]1100    {Rappture::SWITCH_INT, "-bitrate", "value",
[3559]1101     offsetof(FlowVideoSwitches, bitRate), 0},
[1515]1102    {Rappture::SWITCH_CUSTOM, "-format", "string",
[3559]1103     offsetof(FlowVideoSwitches, formatObjPtr), 0, 0, &videoFormatSwitch},
[1515]1104    {Rappture::SWITCH_FLOAT, "-framerate", "value",
[3559]1105     offsetof(FlowVideoSwitches, frameRate), 0},
[1515]1106    {Rappture::SWITCH_INT, "-height", "integer",
[3559]1107     offsetof(FlowVideoSwitches, height), 0},
[1515]1108    {Rappture::SWITCH_INT, "-numframes", "count",
[3559]1109     offsetof(FlowVideoSwitches, numFrames), 0},
[1515]1110    {Rappture::SWITCH_INT, "-width", "integer",
[3559]1111     offsetof(FlowVideoSwitches, width), 0},
[1515]1112    {Rappture::SWITCH_END}
1113};
1114
[3608]1115static bool
1116MakeImageFiles(char *tmpFileName,
1117               int width, int height, int numFrames,
1118               bool *cancelPtr)
[3559]1119{
1120    struct pollfd pollResults;
[3605]1121    pollResults.fd = fileno(nv::g_fIn);
[3559]1122    pollResults.events = POLLIN;
1123#define PENDING_TIMEOUT          10  /* milliseconds. */
1124    int timeout = PENDING_TIMEOUT;
1125
[1508]1126    int oldWidth, oldHeight;
[2877]1127    oldWidth = NanoVis::winWidth;
1128    oldHeight = NanoVis::winHeight;
[1429]1129
[3608]1130    if (width != oldWidth ||
1131        height != oldHeight) {
[2875]1132        // Resize to the requested size.
[3608]1133        NanoVis::resizeOffscreenBuffer(width, height);
[1505]1134    }
[3577]1135    NanoVis::resetFlows();
[3559]1136    *cancelPtr = false;
[3608]1137    bool result = true;
[3559]1138    size_t length = strlen(tmpFileName);
[3608]1139    for (int i = 1; i <= numFrames; i++) {
[3559]1140        if (((i & 0xF) == 0) && (poll(&pollResults, 1, timeout) > 0)) {
[2875]1141            /* If there's another command on stdin, that means the client is
1142             * trying to cancel this operation. */
[3559]1143            *cancelPtr = true;
[2875]1144            break;
1145        }
[3630]1146
1147        NanoVis::licRenderer->convolve();
[3577]1148        NanoVis::advectFlows();
[2930]1149
1150        int fboOrig;
1151        glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fboOrig);
1152
1153        NanoVis::bindOffscreenBuffer();
[3497]1154        NanoVis::render();
[2877]1155        NanoVis::readScreen();
[2930]1156
1157        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboOrig);
1158
[3559]1159        sprintf(tmpFileName + length, "/image%d.ppm", i);
[3605]1160        result = nv::writePPMFile(tmpFileName, NanoVis::screenBuffer,
[3608]1161                                  width, height);
1162        if (!result) {
[3559]1163            break;
1164        }
[1429]1165    }
[3608]1166    if (width != oldWidth ||
1167        height != oldHeight) {
[3559]1168        NanoVis::resizeOffscreenBuffer(oldWidth, oldHeight);
1169    }
1170    tmpFileName[length] = '\0';
[3577]1171    NanoVis::resetFlows();
[3559]1172    return result;
1173}
[1522]1174
[3559]1175static int
1176MakeMovie(Tcl_Interp *interp, char *tmpFileName, const char *token,
1177          FlowVideoSwitches *switchesPtr)
1178{
1179#ifndef FFMPEG
1180#  define FFMPEG "/usr/bin/ffmpeg"
1181#endif
1182    /* Generate the movie from the frame images by exec-ing ffmpeg */
1183    /* The ffmpeg command is
1184     *   ffmpeg -f image2 -i /var/tmp/xxxxx/image%d.ppm                 \
1185     *      -b bitrate -f framerate /var/tmp/xxxxx/movie.mpeg
1186     */
1187    char cmd[BUFSIZ];
[3579]1188    sprintf(cmd, "%s -f image2 -i %s/image%%d.ppm -f %s -b %d -r %f -",
[3559]1189            FFMPEG, tmpFileName, Tcl_GetString(switchesPtr->formatObjPtr),
1190            switchesPtr->bitRate, switchesPtr->frameRate);
[3578]1191    TRACE("Enter: %s", cmd);
[3559]1192    FILE *f;
1193    f = popen(cmd, "r");
1194    if (f == NULL) {
1195        Tcl_AppendResult(interp, "can't run ffmpeg: ",
1196                         Tcl_PosixError(interp), (char *)NULL);
1197        return TCL_ERROR;
1198    }
1199    Rappture::Buffer data;
1200    size_t total = 0;
1201    for (;;) {
1202        ssize_t numRead;
1203        char buffer[BUFSIZ];
1204       
1205        numRead = fread(buffer, sizeof(unsigned char), BUFSIZ, f);
1206        total += numRead;
1207        if (numRead == 0) {             // EOF
1208            break;
1209        }
1210        if (numRead < 0) {              // Error
[3578]1211            ERROR("Can't read movie data: %s",
[3559]1212                  Tcl_PosixError(interp));
1213            Tcl_AppendResult(interp, "can't read movie data: ",
1214                Tcl_PosixError(interp), (char *)NULL);
[2875]1215            return TCL_ERROR;
1216        }
[3559]1217        if (!data.append(buffer, numRead)) {
[3578]1218            ERROR("Can't append movie data to buffer %d bytes",
[3559]1219                  numRead);
1220            Tcl_AppendResult(interp, "can't append movie data to buffer",
1221                             (char *)NULL);
1222            return TCL_ERROR;
1223        }
1224    }
[3578]1225    if (data.size() == 0) {
1226        ERROR("ffmpeg returned 0 bytes");
1227    }
[3584]1228    // Send zero length to client so it can deal with error
1229    sprintf(cmd,"nv>image -type movie -token \"%s\" -bytes %lu\n",
1230            token, (unsigned long)data.size());
[3605]1231    nv::sendDataToClient(cmd, data.bytes(), data.size());
[3584]1232    return TCL_OK;
[3559]1233}
[1429]1234
[3559]1235static int
1236FlowVideoOp(ClientData clientData, Tcl_Interp *interp, int objc,
1237            Tcl_Obj *const *objv)
1238{
1239    FlowVideoSwitches switches;
1240    const char *token;
1241
1242    token = Tcl_GetString(objv[2]);
1243    switches.frameRate = 25.0f;                // Default frame rate 25 fps
1244    switches.bitRate = 6000000;                // Default video bit rate.
1245    switches.width = NanoVis::winWidth;
1246    switches.height = NanoVis::winHeight;
1247    switches.numFrames = 100;
1248    switches.formatObjPtr = Tcl_NewStringObj("mpeg1video", 10);
1249    Tcl_IncrRefCount(switches.formatObjPtr);
[3597]1250    if (Rappture::ParseSwitches(interp, flowVideoSwitches,
[3559]1251                objc - 3, objv + 3, &switches, SWITCH_DEFAULTS) < 0) {
1252        return TCL_ERROR;
[1429]1253    }
[3559]1254    if ((switches.width < 0) || (switches.width > SHRT_MAX) ||
1255        (switches.height < 0) || (switches.height > SHRT_MAX)) {
1256        Tcl_AppendResult(interp, "bad dimensions for video", (char *)NULL);
1257        return TCL_ERROR;
[1501]1258    }
[3559]1259    if ((switches.frameRate < 0.0f) || (switches.frameRate > 30.0f)) {
1260        Tcl_AppendResult(interp, "bad frame rate.", (char *)NULL);
[2875]1261        return TCL_ERROR;
[1429]1262    }
[3559]1263    if (switches.bitRate < 0) {
1264        Tcl_AppendResult(interp, "bad bit rate.", (char *)NULL);
1265        return TCL_ERROR;
1266    }
1267    if (NanoVis::licRenderer == NULL) {
1268        Tcl_AppendResult(interp, "no lic renderer.", (char *)NULL);
1269        return TCL_ERROR;
1270    }
1271    TRACE("FLOW started");
1272
1273    char *tmpFileName;
1274    char nameTemplate[200];
1275    strcpy(nameTemplate,"/var/tmp/flowXXXXXX");
1276    tmpFileName = mkdtemp(nameTemplate);
1277    int result = TCL_OK;
1278    if (tmpFileName == NULL) {
1279        Tcl_AppendResult(interp, "can't create temporary directory \"",
1280                         nameTemplate, "\" for frame image files: ",
1281                         Tcl_PosixError(interp), (char *)NULL);
1282        return TCL_ERROR;
1283    }
1284    size_t length = strlen(tmpFileName);
1285    bool canceled = false;
[3608]1286    if (MakeImageFiles(tmpFileName,
1287                       switches.width, switches.height, switches.numFrames,
1288                       &canceled)) {
1289        result = TCL_OK;
1290    } else {
1291        result = TCL_ERROR;
1292    }
[3559]1293    if ((result == TCL_OK) && (!canceled)) {
1294        result = MakeMovie(interp, tmpFileName, token, &switches);
1295    }
1296    for (int i = 1; i <= switches.numFrames; i++) {
1297        sprintf(tmpFileName + length, "/image%d.ppm", i);
1298        unlink(tmpFileName);
[3605]1299    }
[3559]1300    tmpFileName[length] = '\0';
1301    rmdir(tmpFileName);
[3597]1302    Rappture::FreeSwitches(flowVideoSwitches, &switches, 0);
[3559]1303    return result;
[1429]1304}
[2070]1305#else
[2875]1306/**
1307 *  Not implemented
1308 */
[2070]1309static int
1310FlowVideoOp(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]1311            Tcl_Obj *const *objv)
[2070]1312{
[2875]1313    return TCL_OK;
[2070]1314}
[2875]1315#endif /* HAVE_FFMPEG */
[1429]1316
1317static Rappture::CmdSpec flowCmdOps[] = {
1318    {"add",      1, FlowAddOp,     3, 0, "name ?option value...?",},
[3567]1319    {"delete",   1, FlowDeleteOp,  3, 0, "name ?name...?",},
[1431]1320    {"exists",   1, FlowExistsOp,  3, 3, "name",},
[1467]1321    {"goto",     1, FlowGotoOp,    3, 3, "nSteps",},
[3567]1322    {"names",    1, FlowNamesOp,   2, 2, "",},
[1429]1323    {"next",     2, FlowNextOp,    2, 2, "",},
1324    {"reset",    1, FlowResetOp,   2, 2, "",},
[1517]1325    {"video",    1, FlowVideoOp,   3, 0, "token ?switches...?",},
[1429]1326};
1327static int nFlowCmdOps = NumCmdSpecs(flowCmdOps);
1328
1329static int
1330FlowCmdProc(ClientData clientData, Tcl_Interp *interp, int objc,
[2875]1331            Tcl_Obj *const *objv)
[1429]1332{
1333    Tcl_ObjCmdProc *proc;
1334
1335    proc = Rappture::GetOpFromObj(interp, nFlowCmdOps, flowCmdOps,
[2875]1336                                  Rappture::CMDSPEC_ARG1, objc, objv, 0);
[1429]1337    if (proc == NULL) {
[2875]1338        return TCL_ERROR;
[1429]1339    }
1340    return (*proc) (clientData, interp, objc, objv);
1341}
1342
[2875]1343/**
[3567]1344 *\brief This procedure is invoked to initialize the "flow" command.
[1429]1345 *
1346 * Side effects:
[2875]1347 *    Creates the new command and adds a new entry into a global Tcl
1348 *    associative array.
[1429]1349 */
[3605]1350void
[3611]1351nv::FlowCmdInitProc(Tcl_Interp *interp, ClientData clientData)
[1429]1352{
[3605]1353    Tcl_CreateObjCommand(interp, "flow", FlowCmdProc, clientData, NULL);
[1429]1354}
Note: See TracBrowser for help on using the repository browser.