[vtkusers] Trouble with added features to a VTK Animation: saving frames, "live" vtkPlot, vtkPoints

fitze fitzeq at gmail.com
Tue Aug 16 17:38:10 EDT 2011


I am working on a GUI for a bicycle simulation program in C++ using Qt for
the GUI and VTK for all the fancy fun stuff. The program plays an animation
of a bicycle in motion.

I have the animation working with VTK, but I would like some additional
features with the video.

Feature A - Save the video to image files
Feature B - Update a vtk plot, just below the video in another QVTKWidget,
of various quantitites that are evolving as the simulation progresses, e.g.
X Y position of the bike.
Feature C - Draw a polyline "trace" in the animation that traces the path of
the bike.

These features are not working properly for me right now. I understand that
there are various ways to do animations in VTK and perhaps the way I have
chosen is not adaptable to do those features above. As I see it, there are
three ways to do animations in VTK.

(1) Initialize all actors, then just createa simple loop that changes the
position/orientation of the actors, and just re-render.
(2) Subclass vtkCommand and create a repeating timer on the interactor
(http://www.vtk.org/Wiki/VTK/Examples/Cxx/Utilities/Animation).
(3) Use vtk animation cue's
(http://www.vtk.org/Wiki/VTK/Examples/Cxx/Utilities/AnimationScene).

I have pursued (2), as (1) did not work at all for me, and (3) seemed way
complicated.

Now, I present minimalized code for my implementation of (2), first without
the features I would like, then with my attempt to implement those features.
I will then explain the way in which the features do not work, with the
additional lines labeled as to which Feature they are for.


=================================================
Implementation of (2) without additional features
=================================================

IN MYQWIDGET.H
--------------

class vtkTimerCallback2; // defined below in this file

class MyQWidget() : public QWidget
{
    Q_OBJECT;

    public:
        MyQWidget(bicycle *bike1, QWidget *parent = 0);
    
    private slots:
        void startSimSlot(void);
    
    private:
        // my own class that holds data about the bike,
        // has vtk sources, actors, etc.
        bicycle *bike; 
    
        QGridLayout MyLayout;
        QToolButton *startSimButton;
        QVTKWidget *MyQVTKWidget;
        vtkSmartPointer MyRenderer;
        vtkSmartPointer MyRenderWindow;
        vtkSmartPointer MyCallback;
};

class vtkTimerCallback2 : public vtkCommand {
    public:
        static vtkTimerCallback *New()
        {
            vtkTimerCallback2 *cb = new vtkTimerCallback2;
            cb->TimerCount = 0;
            return cb;
        }
        
        virtual void Execute(vtkObject *caller, unsigned long eventId, 
            void *vtkNotUsed(callData))
        {
            if (vtkCommand::TimerEvent == eventId)
                ++this->TimerCount;
            }
            time = (double)TimerCount/bike->fps;
            bike->UpdateSim(time);
            vtkRenderWindowInteractor *iren =
vtkRenderWindowInteractor::SafeDownCast(caller);
            iren->GetRenderWindow()->Render();
        }
        
        bicycle bike;
    
    private:
        int TimerCount;
        double time;
};
            
IN MYQWIDGET.CPP
----------------
MyQWidget::MyQWidget(bicycle *bike1, QWidget *parent) : QWidget(parent)
{
    bike = bike1;
    
    // qt initialize
    MyLayout = new QGridLayout(this);
    MyQVTKWidget = new QVTKWidget(this);
    startSimButton = new QToolButton;
    
    // qt commands
    setLayout(MyLayout);
    MyLayout->addWidget(MyQVTKWidget,0,0);

    // vtk initialize
    MyRenderer = vtkRenderer::New();
    MyRenderWindow = vtkRenderWindow::New();
    MyCallback = vtkSmartPointer::New();
    
    // vtk commands
    MyRenderWindow->AddRenderer(MyRenderer);
    MyQVTKWidget->SetRenderWindow(MyRenderWindow);
    MyCallback->bike = bike;
    MyQVTKWidget->GetInteractor()->AddObservor(vtkCommand::TimerEvent,
MyCallback);
    
    bike->initSim(MyRenderer);
}

MyQWidget::startSimSlot(void)
{
    bike->UpdateSim(0); // 0 is the (initial) time in seconds of the
animation
    MyQVTKWidget->GetInteractor()->Initialize();
    int timerId =
MyQVTKWidget->GetInteractor()->CreateRepeatingTimer(1000/bike->fps);
    MyQVTKWidget->GetInteractor()->Start();
}

IN BICYCLE.H
------------
class bicycle
{
    public:
        bicycle();
        void initSim(vtkSmartPointer ren);
        void UpdateSim(double time);
        int fps; // framerate of simulation
        integrationstep(time); // lets say all the bike physics is contained
here
                               // im not gonna show any such definition here
        
    private:
        // lets say the bike's configuration is defined by just x and y
coordinates
        double X; // bike's x position
        double Y; // bike's y position
        
        // vtk
        vtkSmartPointer renderer;
        // for simplicity the bike consists of only a sphere
        vtkSmartPointer sphereSource;
        vtkSmartPointer sphereTransform;
        vtkSmartPointer sphereFilter;
        vtkSmartPointer sphereMapper;
        vtkSmartPointer sphereActor;
}

IN BICYCLE.CPP
--------------

bicycle::bicycle()
{
    fps = 100;
}

void bicycle::initSim(vtkSmartPointer ren)
{
    renderer = ren;
    
    sphereSource = vtkSphereSource::New();
    sphereTransform = vtkTransform::New();
    sphereFilter = vtkTransformPolyDataFilter::New();
    sphereMapper = vtkPolyDataMapper::New();
    sphereActor = vtkActor::New();
    
    sphereSource->Update();
    sphereTransform->Translate(.5,.5,0); // orienting the bike, dummy values
    sphereFilter->SetInputConnection(sphereSource->GetOutputPort());
    sphereFilter->SetTransform(sphereTransform);
    sphereMapper->SetInputConnection(sphereFilter->GetOutputPort());
    sphereActor->SetMapper(sphereMapper);
}

void bicycle::UpdateSim(time)
{
    // update bike's X and Y from differential equation integration
    integrationstep(time);
    
    sphereActor->SetPosition(X,Y);
}

==============================================
Implementation of (2) WITH additional features
==============================================

IN MYQWIDGET.H
--------------

class vtkTimerCallback2; // defined below in this file

class MyQWidget() : public QWidget
{
    Q_OBJECT;

    public:
        MyQWidget(bicycle *bike1, QWidget *parent = 0);
    
    private slots:
        void startSimSlot(void);
    
    private:
        // my own class that holds data about the bike,
        // has vtk sources, actors, etc.
        bicycle *bike; 
    
        QGridLayout MyLayout;
        QToolButton *startSimButton;
        QVTKWidget *MyQVTKWidget;
        vtkSmartPointer MyRenderer;
        vtkSmartPointer MyRenderWindow;
        vtkSmartPointer MyCallback;
        
        // Feature A
        vtkSmartPointer VidWriter;
        vtkSmartPointer VidW2I;
        // end Feature A
        
        // Feature B
        QVTKWidget *PlotQVTKWidget;
        vtkSmartPointer PlotView;
        vtkSmartPointer PlotChart;
        // end Feature B
};

class vtkTimerCallback2 : public vtkCommand {
    public:
        static vtkTimerCallback *New()
        {
            vtkTimerCallback2 *cb = new vtkTimerCallback2;
            cb->TimerCount = 0;
            return cb;
        }
        
        virtual void Execute(vtkObject *caller, unsigned long eventId, 
            void *vtkNotUsed(callData))
        {
            if (vtkCommand::TimerEvent == eventId)
                ++this->TimerCount;
            }
            time = (double)TimerCount/bike->fps;
            bike->UpdateSim(time);
            vtkRenderWindowInteractor *iren =
vtkRenderWindowInteractor::SafeDownCast(caller);
            iren->GetRenderWindow()->Render();
            
            // Feature A
            VidW2I->SetInput(iren->GetRenderWindow());
            VidWriter->SetInput(VidW2I->GetOutput());
            VidWrter->SetFileName(QString("bikeimg +
QString("%1").arg(TimerCount) +
                ".ps").toStdString().c_str());
            VidWriter->Write();
            // end Feature A
            
            // Feature B
            bike->SetSimValues(TimerCount); // puts X and Y into a vtkTable
in bike.
            PlotRenWin->Render(); // attempt 1 to update plot
            PlotQVTKWidget->GetInteractor()->Initialize(); // attempt 2
            PlotQVTKWidget->GetInteractor()->Start(); // attempt 2
            PlotChart->Paint(PlotView->GetContext()); // attempt 3
            PlotView->GetScene()->Paint(PlotView->GetContext()); // attempt
4
            // end Feature B
            
        }
        
        bicycle bike;
        
        vtkSmartPointer VidWriter; // Feature A
        vtkSmartPointer VidW2I; // Feature A
        
        QVTKWidget *PlotQVTKWidget; // Feature B
        vtkSmartPointer PlotRenWn; // Feature B
        vtkSmartPointer PlotChart; // Feature B
        vtkSmartPointer PlotView; // Featur B
    
    private:
        int TimerCount;
        double time;
        
};
            
IN MYQWIDGET.CPP
----------------
MyQWidget::MyQWidget(bicycle *bike1, QWidget *parent) : QWidget(parent)
{
    bike = bike1;
    
    // qt initialize
    MyLayout = new QGridLayout(this);
    MyQVTKWidget = new QVTKWidget(this);
    startSimButton = new QToolButton;
    
    // qt commands
    setLayout(MyLayout);
    MyLayout->addWidget(MyQVTKWidget,0,0);

    // vtk initialize
    MyRenderer = vtkRenderer::New();
    MyRenderWindow = vtkRenderWindow::New();
    MyCallback = vtkSmartPointer::New();
    
    // vtk commands
    MyRenderWindow->AddRenderer(MyRenderer);
    MyQVTKWidget->SetRenderWindow(MyRenderWindow);
    MyCallback->bike = bike;
    MyQVTKWidget->GetInteractor()->AddObservor(vtkCommand::TimerEvent,
MyCallback);
    
    bike->initSim(MyRenderer);
    
    // Feature A
    VidWriter = vtkSmartPointer::New();
    VidW2I = vtkSmartPointer::New();
    MyCallback->VidWriter = VidWriter;
    MyCallback->VidW2I = VidW2I;
    // end Feature A
    
    // Feature B
    PlotQVTKWidget = new QVTKWidget(this);
    PlotView = vtkSmartPointer::New();
    PlotChart = vtkSmartPointer::New();
    PlotQVTKWidget->GetRenderWindow()->Render();
    MyLayout->addWidget(PlotQVTKWidget,1,0);
    
    PlotView->SetInteractor(PlotQVTKWidget->GetInteractor());
    PlotQVTKWidget->SetRenderWindow(PlotView->GetRenderWindow());
    PlotView->GetScene()->AddItem(PlotChart);
    
    PlotChart->AddPlot(vtkChart::Line);
    PlotChart->GetPlot(0)->SetInput(bike->SimTable, 0, 1); // X data
    PlotChart->GetPlot(1)->SetInput(bike->SimTable, 0, 2); // Y data
    bike->SimTable->Update();
    PlotChart->Update();
    PlotQVTKWidget->GetInteractor()->Initialize();
    PlotQVTKWidget->GetRenderWindow()->Render();
    PlotQVTKWidget->GetInteractor()->Start();
    
    MyCallback->PlotQVTKWidget = PlotQVTKWidget;
    MyCallback->PlotRenWin = PlotQVTKWidget->GetRenderWindow();
    // end Feature B
}

MyQWidget::startSimSlot(void)
{
    bike->UpdateSim(0); // 0 is the time, initial time
    MyQVTKWidget->GetInteractor()->Initialize();
    int timerId =
MyQVTKWidget->GetInteractor()->CreateRepeatingTimer(1000/bike->fps);
    MyQVTKWidget->GetInteractor()->Start();
    
    
}

IN BICYCLE.H
------------
class bicycle
{
    public:
        bicycle();
        void initSim(vtkSmartPointer ren);
        void UpdateSim(double time);
        int fps; // framerate of simulation
        integrationstep(time); // lets say all the bike physics is contained
here
                               // im not gonna show any such definition here
                               
        // Feature B
        void SetSimValues(int rowidx); // called in vtkTimerCallback2,
stores
                                       // new X and Y data for plotting
        // end Feature B
        
    private:
        // lets say the bike's configuration is defined by just x and y
coordinates
        double t; // current time of the simulation
        double X; // bike's x position
        double Y; // bike's y position
        
        // vtk
        vtkSmartPointer renderer;
        // for simplicity the bike consists of only a sphere
        vtkSmartPointer sphereSource;
        vtkSmartPointer sphereTransform;
        vtkSmartPointer sphereFilter;
        vtkSmartPointer sphereMapper;
        vtkSmartPointer sphereActor;
        
        // Feature B
        vtkSmartPointer PlotTable;
        std::vector< vtkSmartPointer > PlotArrays;
        // end Feature B
        
        // Feature C
        vtkSmartPointer TracePoints;
        vtkSmartPointer TraceLine;
        vtkSmartPointer TraceCell;
        vtkSmartPointer TraceData;
        vtkSmartPointer TraceMapper;
        vtkSmartPointer TraceActor;
        // end Feature C
        
}

IN BICYCLE.CPP
--------------

bicycle::bicycle()
{
    fps = 100;
    
    // Feature B: initialize vtk objects for vtk plot of X and Y
    PlotTable = vtkSmartPointer::New();
    PlotArrays.resize(2); // X column, Y column
    PlotArrays[0] = vtkSmartPointer::New();
    PlotArrays[0]->SetName("X");
    PlotArrays[1] = vtkSmartPointer::New();
    PlotArrays[1]->SetName("Y");
    
    PlotTable->AddColumn(PlotArrays[0]);
    PlotTable->AddColumn(PlotArrays[1]);
    
    PlotTable->InsertNextBlankRow();
    PlotTable->InsertNextBlankRow(1); // vtk plot needs to start with 2
points to
                                      // draw a line    
    // end Feature B
}

void bicycle::initSim(vtkSmartPointer ren)
{
    renderer = ren;
    
    sphereSource = vtkSphereSource::New();
    sphereTransform = vtkTransform::New();
    sphereFilter = vtkTransformPolyDataFilter::New();
    sphereMapper = vtkPolyDataMapper::New();
    sphereActor = vtkActor::New();
    
    sphereSource->Update();
    sphereTransform->Translate(.5,.5,0); // orienting the bike, dummy values
    sphereFilter->SetInputConnection(sphereSource->GetOutputPort());
    sphereFilter->SetTransform(sphereTransform);
    sphereMapper->SetInputConnection(sphereFilter->GetOutputPort());
    sphereActor->SetMapper(sphereMapper);
    
    // Feature C: initialize vtk objects for the bike's polyline trace
    TracePoints = vtkSmartPointer:New();
    TraceLine = vtkSmartPointer::New();
    TraceCell = vtkSmartPointer::New();
    TraceMapper = vtkSmartPointer::New();
    TraceActor = vtkSmartPointer::New();
    TraceData = vtkSmartPointer::New();
    
    TracePoints->SetNumberOfPoints(1000);
    TraceLine->GetPointIds()->SetNumberOfIds(1000);
    // initializing points to values just to ensure that a polyline shows up
in the
    // render window
    for (unsigned int i = 0; i < 1000; i++) {
        TracePoints->SetPoint(i, i, 0, 0);
        TraceLine->GetPointIds()->SetId(i, i);
    }
    TraceCell->InsertNextCell(TraceLine);
    TraceData->SetPoints(TracePoints);
    TraceMapper->SetInput(TraceData);
    TraceActor->SetMapper(TraceMapper);
    // end Feature C
}

void bicycle::UpdateSim(time)
{
    // update bike's X and Y from differential equation integration
    integrationstep(time);
    
    sphereActor->SetPosition(X,Y);
    
    // Feature C
    // adds a point to the trace of the current X Y position of the bike.
the
    // frame number of the animation is also the number of rows in the plot
table
    TracePoints->SetPoint(PlotTable->GetNumberOfRows(), X, Y, 0); 
    TraceMapper->Update();
    // end Feature C
}

// Feature B: called at every frame by the vtkTimerCallback2
void bicycle::SetSimValues(int rowidx) {
    PlotTable->InsertNextBlankRow();
    
    PlotTable->SetValue(rowidx, 0, bike->t); // lets say the bicycle has a t
field holding time
    PlotTable->SetValue(rowidx, 1, bike->X);
    PlotTable->SetValue(rowidx, 2, bike->Y);
    
    PlotTable->Update();
}
// end Feature B

=================== end of code
        
Note that the first implementation, without the features, works just fine.
However, the code above will not work if you were to create the files and
try to compile them; as I've left out the integration function (all the
physics) and the rest of the Qt code, etc. I now describe the way in which
all the features, A B and C, do not work:

Feature A - Images are saved with each updated frame, but the image is
ALWAYS of the first frame of the animation.
    The saved images do not update, though the actual animation does
update/succeed. Perhaps one thing to do, which is the long term goal anyway,
is to move the writing of image files to a separate Slot that is activated
by a QToolButton. Are there any good resources/examples for how to do this
"behind the scenes", e.g. so that the user does not see the video replay
whilst writing image files of the already-played animation?

Feature B - The plot of the X Y data is not displaying anything as the
animation evolves.
    I know that the vtkTable is filling up with the X and Y values that
evolve during the simulation, but the plot that is hooked up to the table is
not updating "live" as the animation evolves. I know that in general the
plot works because I tried rearranging the code so that the X Y data was
plotted once I stopped the animation (by destroying the timer on the
interactor), and this resulted in a successful plot of the X Y data. It
won't update "live" though. Is one option to have the animation and plot be
part of the same QVTKWidget and RenderWindow, and have two renderers in that
one render window? I would say this is not the desired solution because it
fixes the placement of the plot.

Feature C - The trace that I initialize, a very long straight line, IS
drawn. However, this original trace remains the same throughout the
animation; it is not updated EVEN THOUGH those initial straight-line values
are replaced with actual X, Y data at every frame.
    I tried to create a function, bicycle::UpdateTrace(), called at every
new frame, that filled the entire TracePoints object with all the points
again (didn't append just the most recent new point). I'm aware that after
1000 animation frames (I only gave TracePoints 1000 points) I should get a
segfault, but lets ignore that for now.

I have been told that this mailing list is very good about helping
inexperienced people like me, but I haven't posted here before and I'm not
sure how much detail you need to understand the code above. If necessary,
please ask me to provide some prose about how the code works. I'll gladly do
so! I'll try to be responsive and clear.

If you would like to help me, I would be extremely grateful if you could
address only one of the three issues (do not feel like you need to address
all 3 issues/features).

Thank you for your time!


--
View this message in context: http://vtk.1045678.n5.nabble.com/Trouble-with-added-features-to-a-VTK-Animation-saving-frames-live-vtkPlot-vtkPoints-tp4705911p4705911.html
Sent from the VTK - Users mailing list archive at Nabble.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.vtk.org/pipermail/vtkusers/attachments/20110816/a5dd0f4a/attachment.htm>


More information about the vtkusers mailing list