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

Last change on this file since 3935 was 3935, checked in by ldelgass, 6 years ago

First pass at loading VTK vector data for flows in nanovis

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