source: trunk/packages/vizservers/nanovis/FlowCmds.cpp @ 1374

Last change on this file since 1374 was 1374, checked in by gah, 13 years ago
File size: 28.5 KB
Line 
1
2/*
3 * ----------------------------------------------------------------------
4 * Command.cpp
5 *
6 *      This modules creates the Tcl interface to the nanovis server.  The
7 *      communication protocol of the server is the Tcl language.  Commands
8 *      given to the server by clients are executed in a safe interpreter and
9 *      the resulting image rendered offscreen is returned as BMP-formatted
10 *      image data.
11 *
12 * ======================================================================
13 *  AUTHOR:  Wei Qiao <qiaow@purdue.edu>
14 *           Michael McLennan <mmclennan@purdue.edu>
15 *           Purdue Rendering and Perceptualization Lab (PURPL)
16 *
17 *  Copyright (c) 2004-2006  Purdue Research Foundation
18 *
19 *  See the file "license.terms" for information on usage and
20 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21 * ======================================================================
22 */
23
24/*
25 * TODO:  In no particular order...
26 *        x Convert to Tcl_CmdObj interface. (done)
27 *        o Use Tcl command option parser to reduce size of procedures, remove
28 *          lots of extra error checking code. (almost there)
29 *        o Convert GetVolumeIndices to GetVolumes.  Goal is to remove
30 *          all references of Nanovis::volume[] from this file.  Don't
31 *          want to know how volumes are stored. Same for heightmaps.
32 *        o Rationalize volume id scheme. Right now it's the index in
33 *          the vector. 1) Use a list instead of a vector. 2) carry
34 *          an id field that's a number that gets incremented each new volume.
35 *        x Create R2, matrix, etc. libraries. (done)
36 *        o Add bookkeeping for volumes, heightmaps, flows, etc. to track
37 *          1) id #  2) simulation # 3) include/exclude.  The include/exclude
38 *          is to indicate whether the item should contribute to the overall
39 *          limits of the axes.
40 */
41
42#include <assert.h>
43#include <stdlib.h>
44#include <tcl.h>
45#include "Switch.h"
46#include <RpField1D.h>
47#include <RpFieldRect3D.h>
48#include <RpFieldPrism3D.h>
49#include <RpEncode.h>
50#include <RpOutcome.h>
51#include <RpBuffer.h>
52#include <RpAVTranslate.h>
53
54#include "Trace.h"
55#include "Command.h"
56#include "nanovis.h"
57#include "CmdProc.h"
58#include "Nv.h"
59#include "PointSetRenderer.h"
60#include "PointSet.h"
61#include "ZincBlendeVolume.h"
62#include "NvLoadFile.h"
63#include "NvColorTableRenderer.h"
64#include "NvEventLog.h"
65#include "NvZincBlendeReconstructor.h"
66#include "VolumeInterpolator.h"
67#include "HeightMap.h"
68#include "Grid.h"
69#include "NvCamera.h"
70#include "RenderContext.h"
71#include "NvLIC.h"
72#include "Unirect.h"
73
74struct Stream {
75    float position;
76    bool hide;
77    int axis;
78};
79
80struct InitStream {
81    const char *name;
82    Tcl_HashEntry *hashPtr;
83    bool hide;
84    float position;
85};
86
87struct FlowCmd {
88    const char *name;
89    Tcl_HashEntry *hashPtr;
90    Volume *volPtr;
91    Stream streams[3];          // Stream for each axis-aligned plane;
92    bool lic;
93    bool hide;
94    Tcl_HashTable injectTable;
95    Tcl_Command cmdToken;
96};
97
98extern int GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes);
99extern bool load_vector_stream2(Rappture::Outcome &result, int index,
100        size_t nBytes, char *bytes);
101
102extern bool MakeVectorFieldFromUnirect3d(Rappture::Outcome &result,
103        Rappture::Unirect3d &data);
104
105extern int GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
106        bool *boolPtr);
107extern int GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
108        float *floatPtr);
109extern int GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
110        int *axisPtr);
111extern int GetVolumeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
112        Volume **volumePtrPtr);
113
114static Tcl_HashTable flowTable;
115
116static int
117GetFlowFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, FlowCmd **flowPtrPtr)
118{
119    Tcl_HashEntry *hPtr;
120    hPtr = Tcl_FindHashEntry(&flowTable, Tcl_GetString(objPtr));
121    if (hPtr == NULL) {
122        if (interp != NULL) {
123            Tcl_AppendResult(interp, "can't find a flow \"",
124                             Tcl_GetString(objPtr), "\"", (char *)NULL);
125        }
126        return TCL_ERROR;
127    }
128    *flowPtrPtr = (FlowCmd *)Tcl_GetHashValue(hPtr);
129    return TCL_OK;
130}
131
132static int
133FlowDataFileOp(ClientData clientData, Tcl_Interp *interp, int objc,
134               Tcl_Obj *const *objv)
135{
136    Rappture::Outcome result;
137
138    const char *fileName;
139    fileName = Tcl_GetString(objv[3]);
140    Trace("Flow loading data from file %s\n", fileName);
141
142    int extents;
143    if (Tcl_GetIntFromObj(interp, objv[4], &extents) != TCL_OK) {
144        return TCL_ERROR;
145    }
146    Rappture::Buffer buf;
147    if (!buf.load(result, fileName)) {
148        Tcl_AppendResult(interp, "can't load data from \"", fileName, "\": ",
149                         result.remark(), (char *)NULL);
150        return TCL_ERROR;
151    }
152
153    int n = NanoVis::n_volumes;
154    if (strncmp(buf.bytes(), "<DX>", 4) == 0) {
155        if (!load_vector_stream2(result, n, buf.size(), (char *)buf.bytes())) {
156            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
157            return TCL_ERROR;
158        }
159    } else if (strncmp(buf.bytes(), "<unirect3d>", 4) == 0) {
160        Rappture::Unirect3d data;
161        Tcl_CmdInfo cmdInfo;
162
163        /* Set the clientdata field of the unirect3d command to contain
164         * the local data structure. */
165        if (!Tcl_GetCommandInfo(interp, "unirect3d", &cmdInfo)) {
166            return TCL_ERROR;
167        }
168        data.extents(extents);
169        cmdInfo.objClientData = (ClientData)&data;     
170        Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
171        if (Tcl_Eval(interp, (const char *)buf.bytes()) != TCL_OK) {
172            return TCL_ERROR;
173        }
174        if (!data.isInitialized()) {
175            return TCL_ERROR;
176        }
177        if (!MakeVectorFieldFromUnirect3d(result, data)) {
178            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
179            return TCL_ERROR;
180        }
181    }
182    FlowCmd *flowPtr = (FlowCmd *)clientData;
183
184    if (flowPtr->volPtr != NULL) {
185        // You might have to remove the volume from the particle renderer.
186        delete flowPtr->volPtr;
187    }
188    flowPtr->volPtr = NanoVis::volume[NanoVis::n_volumes];
189    //
190    // BE CAREFUL:  Set the number of slices to something
191    //   slightly different for each volume.  If we have
192    //   identical volumes at exactly the same position
193    //   with exactly the same number of slices, the second
194    //   volume will overwrite the first, so the first won't
195    //   appear at all.
196    //
197    flowPtr->volPtr->set_n_slice(256-n);
198    // volPtr->set_n_slice(512-n);
199    flowPtr->volPtr->disable_cutplane(0);
200    flowPtr->volPtr->disable_cutplane(1);
201    flowPtr->volPtr->disable_cutplane(2);
202   
203    NanoVis::vol_renderer->add_volume(flowPtr->volPtr,
204        NanoVis::get_transfunc("default"));
205   
206    Trace("Flow Data move\n");
207    float dx0 = -0.5;
208    float dy0 = -0.5*flowPtr->volPtr->height/flowPtr->volPtr->width;
209    float dz0 = -0.5*flowPtr->volPtr->depth/flowPtr->volPtr->width;
210    flowPtr->volPtr->move(Vector3(dx0, dy0, dz0));
211    return TCL_OK;
212}
213
214static int
215FlowDataFollowsOp(ClientData clientData, Tcl_Interp *interp, int objc,
216                    Tcl_Obj *const *objv)
217{
218    Rappture::Outcome result;
219
220    Trace("Flow Data Loading\n");
221
222    int nbytes;
223    if (Tcl_GetIntFromObj(interp, objv[3], &nbytes) != TCL_OK) {
224        return TCL_ERROR;
225    }
226    int extents;
227    if (Tcl_GetIntFromObj(interp, objv[4], &extents) != TCL_OK) {
228        return TCL_ERROR;
229    }
230    Rappture::Buffer buf;
231    if (GetDataStream(interp, buf, nbytes) != TCL_OK) {
232        return TCL_ERROR;
233    }
234    int n = NanoVis::n_volumes;
235    if (strncmp(buf.bytes(), "<DX>", 4) == 0) {
236        if (!load_vector_stream2(result, n, buf.size(), (char *)buf.bytes())) {
237            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
238            return TCL_ERROR;
239        }
240    } else if (strncmp(buf.bytes(), "<unirect3d>", 4) == 0) {
241        Rappture::Unirect3d data;
242        Tcl_CmdInfo cmdInfo;
243
244        /* Set the clientdata field of the unirect3d command to contain
245         * the local data structure. */
246        if (!Tcl_GetCommandInfo(interp, "unirect3d", &cmdInfo)) {
247            return TCL_ERROR;
248        }
249        data.extents(extents);
250        cmdInfo.objClientData = (ClientData)&data;     
251        Tcl_SetCommandInfo(interp, "unirect3d", &cmdInfo);
252        if (Tcl_Eval(interp, (const char *)buf.bytes()) != TCL_OK) {
253            return TCL_ERROR;
254        }
255        if (!data.isInitialized()) {
256            return TCL_ERROR;
257        }
258        if (!MakeVectorFieldFromUnirect3d(result, data)) {
259            Tcl_AppendResult(interp, result.remark(), (char *)NULL);
260            return TCL_ERROR;
261        }
262    }
263    FlowCmd *flowPtr = (FlowCmd *)clientData;
264
265    if (flowPtr->volPtr != NULL) {
266        delete flowPtr->volPtr;
267    }
268    flowPtr->volPtr = NanoVis::volume[NanoVis::n_volumes];
269    //
270    // BE CAREFUL:  Set the number of slices to something
271    //   slightly different for each volume.  If we have
272    //   identical volumes at exactly the same position
273    //   with exactly the same number of slices, the second
274    //   volume will overwrite the first, so the first won't
275    //   appear at all.
276    //
277    flowPtr->volPtr->set_n_slice(256-n);
278    // volPtr->set_n_slice(512-n);
279    flowPtr->volPtr->disable_cutplane(0);
280    flowPtr->volPtr->disable_cutplane(1);
281    flowPtr->volPtr->disable_cutplane(2);
282   
283    NanoVis::vol_renderer->add_volume(flowPtr->volPtr,
284        NanoVis::get_transfunc("default"));
285   
286    Trace("Flow Data move\n");
287    float dx0 = -0.5;
288    float dy0 = -0.5*flowPtr->volPtr->height/flowPtr->volPtr->width;
289    float dz0 = -0.5*flowPtr->volPtr->depth/flowPtr->volPtr->width;
290    flowPtr->volPtr->move(Vector3(dx0, dy0, dz0));
291    return TCL_OK;
292}
293
294static Rappture::CmdSpec flowDataOps[] = {
295    {"file",    2, FlowDataFileOp,    5, 5, "fileName extents",},
296    {"follows", 2, FlowDataFollowsOp, 5, 5, "size extents",},
297};
298static int nFlowDataOps = NumCmdSpecs(flowDataOps);
299
300static int
301FlowDataOp(ClientData clientData, Tcl_Interp *interp, int objc,
302           Tcl_Obj *const *objv)
303{
304    Tcl_ObjCmdProc *proc;
305
306    proc = Rappture::GetOpFromObj(interp, nFlowDataOps, flowDataOps,
307                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
308    if (proc == NULL) {
309        return TCL_ERROR;
310    }
311    return (*proc) (clientData, interp, objc, objv);
312}
313
314
315static Rappture::SwitchSpec streamSwitches[] =
316{
317    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
318        offsetof(Stream, hide), 0},
319    {Rappture::SWITCH_FLOAT, "-position", "number",
320        offsetof(Stream, position), 0},
321    {Rappture::SWITCH_END}
322};
323
324static int
325FlowStreamConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
326                     Tcl_Obj *const *objv)
327{
328    int axis;
329    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
330        return TCL_ERROR;
331    }
332    FlowCmd *flowPtr = (FlowCmd *)clientData;
333    Stream *streamPtr;
334    streamPtr = flowPtr->streams + axis;
335
336    if (Rappture::ParseSwitches(interp, streamSwitches, objc - 3, objv + 3,
337        streamPtr, SWITCH_DEFAULTS) < 0) {
338        return TCL_ERROR;
339    }
340    NanoVis::lic_slice_x = streamPtr->position;
341    NanoVis::licRenderer->set_axis(axis);
342    NanoVis::licRenderer->set_offset(streamPtr->position);
343    return TCL_OK;
344}
345
346static Rappture::CmdSpec flowStreamOps[] = {
347    {"configure", 1, FlowStreamConfigureOp,  5, 5, "axis ?switches?",},
348};
349
350static int nFlowStreamOps = NumCmdSpecs(flowStreamOps);
351
352static int
353FlowStreamOp(ClientData clientData, Tcl_Interp *interp, int objc,
354             Tcl_Obj *const *objv)
355{
356    Tcl_ObjCmdProc *proc;
357
358    proc = Rappture::GetOpFromObj(interp, nFlowStreamOps, flowStreamOps,
359        Rappture::CMDSPEC_ARG2, objc, objv, 0);
360    if (proc == NULL) {
361        return TCL_ERROR;
362    }
363    return (*proc) (clientData, interp, objc, objv);
364}
365
366static int
367FlowParticlesVisibleOp(ClientData clientData, Tcl_Interp *interp, int objc,
368             Tcl_Obj *const *objv)
369{
370    bool state;
371    if (GetBooleanFromObj(interp, objv[3], &state) != TCL_OK) {
372        return TCL_ERROR;
373    }
374
375    NanoVis::particle_on = state;
376    return TCL_OK;
377}
378
379static Rappture::CmdSpec flowParticlesOps[] = {
380    {"visible",    1, FlowParticlesVisibleOp,  4, 4, "on|off",},
381};
382static int nFlowParticlesOps = NumCmdSpecs(flowParticlesOps);
383
384static int
385FlowParticlesOp(ClientData clientData, Tcl_Interp *interp, int objc,
386             Tcl_Obj *const *objv)
387{
388    Tcl_ObjCmdProc *proc;
389
390    proc = Rappture::GetOpFromObj(interp, nFlowParticlesOps, flowParticlesOps,
391                                  Rappture::CMDSPEC_ARG2, objc, objv, 0);
392    if (proc == NULL) {
393        return TCL_ERROR;
394    }
395    return (*proc) (clientData, interp, objc, objv);
396}
397
398
399static int
400FlowVectorIdOp(ClientData clientData, Tcl_Interp *interp, int objc,
401             Tcl_Obj *const *objv)
402{
403    Volume *volPtr;
404    if (GetVolumeFromObj(interp, objv[2], &volPtr) != TCL_OK) {
405        return TCL_ERROR;
406    }
407    if (NanoVis::flowVisRenderer != NULL) {
408        NanoVis::flowVisRenderer->setVectorField(volPtr->id,
409            *(volPtr->get_location()),
410            1.0f,
411            volPtr->height / (float)volPtr->width,
412            volPtr->depth  / (float)volPtr->width,
413            volPtr->wAxis.max());
414        NanoVis::initParticle();
415    }
416    if (NanoVis::licRenderer != NULL) {
417        NanoVis::licRenderer->setVectorField(volPtr->id,
418            *(volPtr->get_location()),
419            1.0f / volPtr->aspect_ratio_width,
420            1.0f / volPtr->aspect_ratio_height,
421            1.0f / volPtr->aspect_ratio_depth,
422            volPtr->wAxis.max());
423        NanoVis::licRenderer->set_offset(NanoVis::lic_slice_z);
424    }
425    return TCL_OK;
426}
427
428static Rappture::SwitchSpec flowSwitches[] =
429{
430    {Rappture::SWITCH_BOOLEAN, "-lic", "boolean",
431        offsetof(FlowCmd, hide), 0},
432    {Rappture::SWITCH_END}
433};
434
435static int
436FlowConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
437                Tcl_Obj *const *objv)
438{
439    int axis;
440    if (GetAxisFromObj(interp, objv[3], &axis) != TCL_OK) {
441        return TCL_ERROR;
442    }
443    FlowCmd *flowPtr = (FlowCmd *)clientData;
444    if (Rappture::ParseSwitches(interp, flowSwitches, objc - 3, objv + 3,
445        flowPtr, SWITCH_DEFAULTS) < 0) {
446        return TCL_ERROR;
447    }
448    return TCL_OK;
449}
450
451static Rappture::SwitchSpec initStreamSwitches[] =
452{
453    {Rappture::SWITCH_BOOLEAN, "-hide", "boolean",
454        offsetof(InitStream, hide), 0},
455    {Rappture::SWITCH_FLOAT, "-position", "number",
456        offsetof(InitStream, position), 0},
457    {Rappture::SWITCH_END}
458};
459
460static int
461FlowInjectorAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
462                Tcl_Obj *const *objv)
463{
464    FlowCmd *flowPtr = (FlowCmd *)clientData;
465
466    const char *name;
467    name = Tcl_GetString(objv[3]);
468    Tcl_HashEntry *hPtr;
469    int isNew;
470    hPtr = Tcl_CreateHashEntry(&flowPtr->injectTable, name, &isNew);
471    if (!isNew) {
472        Tcl_AppendResult(interp, "flow \"", Tcl_GetString(objv[0]),
473                "\" already has a injection plane named \"", name, "\"",
474                (char *)NULL);
475        return TCL_ERROR;
476    }
477    InitStream *iStreamPtr;
478    iStreamPtr = new InitStream;
479    if (iStreamPtr == NULL) {
480        Tcl_AppendResult(interp, "can't allocate a initstream \"", name,
481                         "\"", (char *)NULL);
482        return TCL_ERROR;
483    }
484    Tcl_SetHashValue(hPtr, iStreamPtr);
485    iStreamPtr->hashPtr = hPtr;
486    iStreamPtr->name = Tcl_GetHashKey(&flowPtr->injectTable, hPtr);
487    if (Rappture::ParseSwitches(interp, initStreamSwitches, objc - 3, objv + 3,
488        iStreamPtr, SWITCH_DEFAULTS) < 0) {
489        return TCL_ERROR;
490    }
491    return TCL_OK;
492}
493
494static int
495FlowInjectorDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
496                   Tcl_Obj *const *objv)
497{
498    FlowCmd *flowPtr = (FlowCmd *)clientData;
499    const char *name;
500    name = Tcl_GetString(objv[3]);
501    Tcl_HashEntry *hPtr;
502    hPtr = Tcl_FindHashEntry(&flowPtr->injectTable, name);
503    if (hPtr == NULL) {
504        Tcl_AppendResult(interp, "can't find a initstream \"", name, "\"",
505                         (char *)NULL);
506        return TCL_ERROR;
507    }
508    InitStream *iStreamPtr = (InitStream *)Tcl_GetHashValue(hPtr);
509    Tcl_DeleteHashEntry(hPtr);
510    delete iStreamPtr;
511    return TCL_OK;
512}
513
514static int
515FlowInjectorNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
516             Tcl_Obj *const *objv)
517{
518    FlowCmd *flowPtr = (FlowCmd *)clientData;
519    Tcl_HashEntry *hPtr;
520    Tcl_HashSearch iter;
521    Tcl_Obj *listObjPtr;
522    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
523    for (hPtr = Tcl_FirstHashEntry(&flowPtr->injectTable, &iter);
524         hPtr != NULL; hPtr = Tcl_NextHashEntry(&iter)) {
525        Tcl_Obj *objPtr;
526        const char *name;
527
528        name = Tcl_GetHashKey(&flowPtr->injectTable, hPtr);
529        objPtr = Tcl_NewStringObj(name, -1);
530        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
531    }
532    Tcl_SetObjResult(interp, listObjPtr);
533    return TCL_OK;
534}
535
536static int
537FlowInjectorConfigureOp(ClientData clientData, Tcl_Interp *interp, int objc,
538                        Tcl_Obj *const *objv)
539{
540    FlowCmd *flowPtr = (FlowCmd *)clientData;
541    const char *name;
542    name = Tcl_GetString(objv[3]);
543    Tcl_HashEntry *hPtr;
544    hPtr = Tcl_FindHashEntry(&flowPtr->injectTable, name);
545    if (hPtr == NULL) {
546        Tcl_AppendResult(interp, "can't find a initstream \"", name, "\"",
547                         (char *)NULL);
548        return TCL_ERROR;
549    }
550    InitStream *iStreamPtr = (InitStream *)Tcl_GetHashValue(hPtr);
551    if (Rappture::ParseSwitches(interp, initStreamSwitches, objc - 4, objv + 4,
552        iStreamPtr, SWITCH_DEFAULTS) < 0) {
553        return TCL_ERROR;
554    }
555    return TCL_OK;
556}
557
558
559/*
560 *---------------------------------------------------------------------------
561 *
562 * FlowInjectorObjCmd --
563 *
564 *      This procedure is invoked to process commands on behalf of the flow
565 *      object.
566 *
567 * Results:
568 *      A standard Tcl result.
569 *
570 * Side effects:
571 *      See the user documentation.
572 *
573 *---------------------------------------------------------------------------
574 */
575static Rappture::CmdSpec flowInjectorOps[] = {
576    {"add",        1, FlowInjectorAddOp,        3, 0, "name ?switches?",},
577    {"configure",  1, FlowInjectorConfigureOp,  3, 5, "name ?switches?",},
578    {"delete",     1, FlowInjectorDeleteOp,     3, 0, "?name...?"},
579    {"names",      1, FlowInjectorNamesOp,      3, 4, "?pattern?"},
580};
581
582static int nFlowInjectorOps = NumCmdSpecs(flowInjectorOps);
583
584static int
585FlowInjectorOp(ClientData clientData, Tcl_Interp *interp, int objc,
586               Tcl_Obj *const *objv)
587{
588    Tcl_ObjCmdProc *proc;
589    proc = Rappture::GetOpFromObj(interp, nFlowInjectorOps, flowInjectorOps,
590        Rappture::CMDSPEC_ARG1, objc, objv, 0);
591    if (proc == NULL) {
592        return TCL_ERROR;
593    }
594    FlowCmd *flowPtr = (FlowCmd *)clientData;
595    Tcl_Preserve(flowPtr);
596    int result;
597    result = (*proc) (clientData, interp, objc, objv);
598    Tcl_Release(flowPtr);
599    return result;
600}
601
602/*
603 *---------------------------------------------------------------------------
604 *
605 * FlowInstObjCmd --
606 *
607 *      This procedure is invoked to process commands on behalf of the flow
608 *      object.
609 *
610 * Results:
611 *      A standard Tcl result.
612 *
613 * Side effects:
614 *      See the user documentation.
615 *
616 *---------------------------------------------------------------------------
617 */
618static Rappture::CmdSpec flowInstOps[] = {
619    {"configure",   1, FlowConfigureOp,  3, 5, "?switches?",},
620    {"data",        1, FlowDataOp,       4, 0, "oper ?args?"},
621    {"injector",    1, FlowInjectorOp,   3, 0, "oper ?args?",},
622    {"particles",   1, FlowParticlesOp,  3, 0, "oper ?args?",},
623    {"stream",      1, FlowStreamOp,     4, 0, "oper axis ?args?",},
624};
625static int nFlowInstOps = NumCmdSpecs(flowInstOps);
626
627static int
628FlowInstObjCmd(ClientData clientData, Tcl_Interp *interp, int objc,
629               Tcl_Obj *const *objv)
630{
631    Tcl_ObjCmdProc *proc;
632    proc = Rappture::GetOpFromObj(interp, nFlowInstOps, flowInstOps,
633        Rappture::CMDSPEC_ARG1, objc, objv, 0);
634    if (proc == NULL) {
635        return TCL_ERROR;
636    }
637    FlowCmd *flowPtr = (FlowCmd *)clientData;
638    Tcl_Preserve(flowPtr);
639    int result;
640    result = (*proc) (clientData, interp, objc, objv);
641    Tcl_Release(flowPtr);
642    return result;
643}
644
645/*
646 *---------------------------------------------------------------------------
647 *
648 * FlowInstDeleteProc --
649 *
650 *      Deletes the command associated with the tree.  This is called only
651 *      when the command associated with the tree is destroyed.
652 *
653 * Results:
654 *      None.
655 *
656 *---------------------------------------------------------------------------
657 */
658static void
659FlowInstDeleteProc(ClientData clientData)
660{
661    FlowCmd *flowPtr = (FlowCmd *)clientData;
662
663    if (flowPtr->hashPtr != NULL) {
664        Tcl_DeleteHashEntry(flowPtr->hashPtr);
665    }
666    delete flowPtr;
667}
668
669/*
670 *---------------------------------------------------------------------------
671 *
672 * FlowAddOp --
673 *
674 *---------------------------------------------------------------------------
675 */
676/*ARGSUSED*/
677static int
678FlowAddOp(ClientData clientData, Tcl_Interp *interp, int objc,
679          Tcl_Obj *const *objv)
680{
681    const char *name;
682    name = Tcl_GetString(objv[2]);
683
684    Tcl_HashEntry *hPtr;
685    int isNew;
686    hPtr = Tcl_CreateHashEntry(&flowTable, name, &isNew);
687    if (!isNew) {
688        Tcl_AppendResult(interp, "flow \"", name, "\" already exists.",
689                         (char *)NULL);
690        return TCL_ERROR;
691    }
692    Tcl_CmdInfo cmdInfo;
693    if (Tcl_GetCommandInfo(interp, name, &cmdInfo)) {
694        Tcl_AppendResult(interp, "an another command \"", name,
695                         "\" already exists.", (char *)NULL);
696        return TCL_ERROR;
697    }   
698    FlowCmd *flowPtr;
699    flowPtr = new FlowCmd;
700    if (flowPtr == NULL) {
701        Tcl_AppendResult(interp, "can't allocate a flow object \"", name,
702                         "\"", (char *)NULL);
703        return TCL_ERROR;
704    }
705    flowPtr->hashPtr = hPtr;
706    flowPtr->name = Tcl_GetHashKey(&flowTable, hPtr);
707    flowPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)name,
708        (Tcl_ObjCmdProc *)FlowInstObjCmd, flowPtr, FlowInstDeleteProc);
709    Tcl_SetHashValue(hPtr, flowPtr);
710
711    flowPtr->volPtr = NULL;
712    return TCL_OK;
713}
714
715/*
716 *---------------------------------------------------------------------------
717 *
718 * FlowDeleteOp --
719 *
720 *---------------------------------------------------------------------------
721 */
722/*ARGSUSED*/
723static int
724FlowDeleteOp(ClientData clientData, Tcl_Interp *interp, int objc,
725             Tcl_Obj *const *objv)
726{
727    int i;
728
729    for (i = 2; i < objc; i++) {
730        FlowCmd *flowPtr;
731
732        if (GetFlowFromObj(interp, objv[i], &flowPtr) != TCL_OK) {
733            return TCL_ERROR;
734        }
735        Tcl_DeleteCommandFromToken(interp, flowPtr->cmdToken);
736        delete flowPtr;
737    }
738    return TCL_OK;
739}
740
741/*
742 *---------------------------------------------------------------------------
743 *
744 * FlowNamesOp --
745 *
746 *---------------------------------------------------------------------------
747 */
748/*ARGSUSED*/
749static int
750FlowNamesOp(ClientData clientData, Tcl_Interp *interp, int objc,
751            Tcl_Obj *const *objv)
752{
753    Tcl_Obj *listObjPtr;
754    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
755
756    Tcl_HashEntry *hPtr;
757    Tcl_HashSearch iter;
758    for (hPtr = Tcl_FirstHashEntry(&flowTable, &iter); hPtr != NULL;
759        hPtr = Tcl_NextHashEntry(&iter)) {
760        FlowCmd *flowPtr;
761        flowPtr = (FlowCmd *)Tcl_GetHashValue(hPtr);
762        const char *name;
763        name = Tcl_GetCommandName(interp, flowPtr->cmdToken);
764        Tcl_Obj *objPtr;
765        objPtr = Tcl_NewStringObj(name, -1);
766        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
767    }
768    Tcl_SetObjResult(interp, listObjPtr);
769    return TCL_OK;
770}
771
772static int
773FlowNextOp(ClientData clientData, Tcl_Interp *interp, int objc,
774             Tcl_Obj *const *objv)
775{
776    if (!NanoVis::licRenderer->isActivated()) {
777        NanoVis::licRenderer->activate();
778    }
779    if (!NanoVis::flowVisRenderer->isActivated()) {
780        NanoVis::flowVisRenderer->activate();
781    }
782
783    Trace("sending flow playback frame\n");
784
785    // Generate the latest frame and send it back to the client
786    if (NanoVis::licRenderer->isActivated()) {
787        NanoVis::licRenderer->convolve();
788    }
789    NanoVis::flowVisRenderer->advect();
790    NanoVis::offscreen_buffer_capture();  //enable offscreen render
791    NanoVis::display();
792    NanoVis::read_screen();
793
794    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
795
796    // NanoVis::bmp_write_to_file(frame_count, fileName);
797    Trace("FLOW end\n");
798    return TCL_OK;
799}
800
801static int
802FlowResetOp(ClientData clientData, Tcl_Interp *interp, int objc,
803             Tcl_Obj *const *objv)
804{
805    NanoVis::initParticle();
806    return TCL_OK;
807}
808
809static int
810FlowVideoOp(ClientData clientData, Tcl_Interp *interp, int objc,
811            Tcl_Obj *const *objv)
812{
813    int width, height;          // Resolution of video.
814    int numFrames;              // Total number of frames.
815    float frameRate;            // Frame rate of the video.
816    float bitRate;              // Bit rate of the vide.
817
818    if ((Tcl_GetIntFromObj(interp, objv[2], &width) != TCL_OK) ||
819        (Tcl_GetIntFromObj(interp, objv[3], &height) != TCL_OK) ||
820        (Tcl_GetIntFromObj(interp, objv[4], &numFrames) != TCL_OK) ||
821        (GetFloatFromObj(interp, objv[5], &frameRate) != TCL_OK) ||
822        (GetFloatFromObj(interp, objv[6], &bitRate) != TCL_OK)) {
823        return TCL_ERROR;
824    }
825    if ((width<0) || (width>SHRT_MAX) || (height<0) || (height>SHRT_MAX)) {
826        Tcl_AppendResult(interp, "bad dimensions for video", (char *)NULL);
827        return TCL_ERROR;
828    }
829    if ((frameRate < 0.0f) || (frameRate > 30.0f)) {
830        Tcl_AppendResult(interp, "bad frame rate \"", Tcl_GetString(objv[5]),
831                         "\"", (char *)NULL);
832        return TCL_ERROR;
833    }
834    if ((bitRate < 0.0f) || (frameRate > 30.0f)) {
835        Tcl_AppendResult(interp, "bad bit rate \"", Tcl_GetString(objv[6]),
836                         "\"", (char *)NULL);
837        return TCL_ERROR;
838    }
839
840    if (NanoVis::licRenderer) {
841        NanoVis::licRenderer->activate();
842    }
843    if (NanoVis::flowVisRenderer) {
844        NanoVis::flowVisRenderer->activate();
845    }
846
847    // Save the old dimensions of the offscreen buffer.
848    int oldWidth, oldHeight;
849    oldWidth = NanoVis::win_width;
850    oldHeight = NanoVis::win_height;
851
852    if ((width != oldWidth) || (height != oldHeight)) {
853        // Resize to the requested size.
854        NanoVis::resize_offscreen_buffer(width, height);
855    }
856
857    char fileName[128];
858    sprintf(fileName,"/tmp/flow%d.mpeg", getpid());
859
860    Trace("FLOW started\n");
861
862    Rappture::Outcome result;
863    Rappture::AVTranslate movie(width, height, frameRate, bitRate);
864
865    int pad = 0;
866    if ((3*NanoVis::win_width) % 4 > 0) {
867        pad = 4 - ((3*NanoVis::win_width) % 4);
868    }
869
870    movie.init(result, fileName);
871
872    for (int i = 0; i < numFrames; i++) {
873        // Generate the latest frame and send it back to the client
874        if (NanoVis::licRenderer &&
875            NanoVis::licRenderer->isActivated()) {
876            NanoVis::licRenderer->convolve();
877        }
878        if (NanoVis::flowVisRenderer &&
879            NanoVis::flowVisRenderer->isActivated()) {
880            NanoVis::flowVisRenderer->advect();
881        }
882        NanoVis::offscreen_buffer_capture();  //enable offscreen render
883        NanoVis::display();
884
885        NanoVis::read_screen();
886        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
887
888        // This is done before bmp_write_to_file because bmp_write_to_file
889        // turns rgb data to bgr
890        movie.append(result, NanoVis::screen_buffer, pad);
891        // NanoVis::bmp_write_to_file(frame_count, fileName);
892    }
893
894    movie.done(result);
895    Trace("FLOW end\n");
896
897    if (NanoVis::licRenderer) {
898        NanoVis::licRenderer->deactivate();
899    }
900    if (NanoVis::flowVisRenderer) {
901        NanoVis::flowVisRenderer->deactivate();
902    }
903    NanoVis::initParticle();
904
905    // FIXME: find a way to get the data from the movie object as a void*
906    Rappture::Buffer data;
907    if (!data.load(result, fileName)) {
908        Tcl_AppendResult(interp, "can't load data from temporary movie file \"",
909                fileName, "\": ", result.remark(), (char *)NULL);
910        return TCL_ERROR;
911    }
912    // Build the command string for the client.
913    char command[200];
914    sprintf(command,"nv>image -bytes %lu -type movie -token token\n",
915            (unsigned long)data.size());
916
917    NanoVis::sendDataToClient(command, data.bytes(), data.size());
918    if (unlink(fileName) != 0) {
919        Tcl_AppendResult(interp, "can't unlink temporary movie file \"",
920                fileName, "\": ", Tcl_PosixError(interp), (char *)NULL);
921        return TCL_ERROR;
922    }
923    return TCL_OK;
924}
925
926/*
927 *---------------------------------------------------------------------------
928 *
929 * FlowObjCmd --
930 *
931 *---------------------------------------------------------------------------
932 */
933static Rappture::CmdSpec flowCmdOps[] = {
934    {"add",      1, FlowAddOp,     2, 3, "?name? ?option value...?",},
935    {"delete",   1, FlowDeleteOp,  2, 0, "name...",},
936    {"names",    1, FlowNamesOp,   2, 3, "?pattern?",},
937    {"next",     2, FlowNextOp,    2, 2, "",},
938    {"reset",    1, FlowResetOp,   2, 2, "",},
939    {"video",    1, FlowVideoOp,   7, 7,       
940        "width height numFrames frameRate bitRate ",},
941};
942static int nFlowCmdOps = NumCmdSpecs(flowCmdOps);
943
944/*ARGSUSED*/
945static int
946FlowCmdProc(ClientData clientData, Tcl_Interp *interp, int objc,
947            Tcl_Obj *const *objv)
948{
949    Tcl_ObjCmdProc *proc;
950
951    proc = Rappture::GetOpFromObj(interp, nFlowCmdOps, flowCmdOps,
952        Rappture::CMDSPEC_ARG1, objc, objv, 0);
953    if (proc == NULL) {
954        return TCL_ERROR;
955    }
956    return (*proc) (clientData, interp, objc, objv);
957}
958
959/*
960 *---------------------------------------------------------------------------
961 *
962 * FlowCmdInitProc --
963 *
964 *      This procedure is invoked to initialize the "tree" command.
965 *
966 * Results:
967 *      None.
968 *
969 * Side effects:
970 *      Creates the new command and adds a new entry into a global Tcl
971 *      associative array.
972 *
973 *---------------------------------------------------------------------------
974 */
975int
976FlowCmdInitProc(Tcl_Interp *interp)
977{
978    Tcl_CreateObjCommand(interp, "flow", FlowCmdProc, NULL, NULL);
979    Tcl_InitHashTable(&flowTable, TCL_STRING_KEYS);
980    return TCL_OK;
981}
Note: See TracBrowser for help on using the repository browser.